你有没有遇到过这样的问题?
👉 想让程序同时做几件事,结果用了 new Thread(),代码乱成一锅粥?
👉 线程明明启动了,为什么数据不对?i++ 居然不准?
👉 面试官问:“说说 Java 多线程?” 你脑子里闪过 synchronized、volatile、Thread,但串不起来,说不完整……
想象一下你在家做饭:
在 Java 世界里:
✅ 多线程 = 让程序“同时”执行多个任务 利用多核 CPU,提升程序吞吐量和响应速度
概念 | 进程(Process) | 线程(Thread) |
|---|---|---|
类比 | 一个“厨房” | 厨房里的“一个厨师” |
资源 | 独立内存空间、文件句柄 | 共享所属进程的资源 |
创建开销 | 大(系统调用) | 小(JVM 内) |
通信 | 复杂(管道、共享内存) | 简单(直接读写共享变量) |
安全性 | 隔离好,一个崩不影响其他 | 共享数据,一个出错可能影响整个进程 |
✅ Java 中,一个进程可以有多个线程,它们共享堆内存,但栈是私有的。
Thread 类(不推荐)public class MyThread extends Thread {
@Override
public void run() {
System.out.println("我是线程:" + Thread.currentThread().getName());
}
}
// 使用
new MyThread().start(); // 必须调用 start(),不是 run()!❌ 缺点:Java 不支持多继承,扩展性差。
Runnable 接口(推荐)public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是线程:" + Thread.currentThread().getName());
}
}
// 使用
Thread t = new Thread(new MyRunnable());
t.start();✅ 优点:解耦,实现类可以继承其他类。
Callable + FutureTask(能返回结果)public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "任务完成!";
}
}
// 使用
FutureTask<String> task = new FutureTask<>(new MyCallable());
new Thread(task).start();
String result = task.get(); // 阻塞等待结果
System.out.println(result);✅ 优点:可以获取线程执行结果,还能抛异常。
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.submit(() -> {
System.out.println("我在线程池中运行:" + Thread.currentThread().getName());
});
// 用完记得关闭!
pool.shutdown();✅ 为什么必须用线程池?

new Thread(),还没调用 start()start(),等待 CPU 调度(就绪 + 运行)sleep()、wait() 等wait()、join()、sleep() 等run() 或异常退出i++ 不安全?int i = 0;
// 10 个线程,每个 ++ 1000 次
for (int j = 0; j < 1000; j++) {
i++;
}结果很可能 ≠ 10000!
因为 i++ 不是原子操作!它分三步:
i 的值两个线程可能同时读到 i=5,都+1,都写回 6,结果只+1,而不是+2。
✅ 线程安全 = 多线程访问共享数据时,结果正确
synchronized:给代码“上锁”// 方式1:修饰方法
public synchronized void add() {
i++;
}
// 方式2:修饰代码块
synchronized (this) {
i++;
}✅ 原理:每个对象有一把“内置锁”(Monitor),拿到锁才能执行。
volatile:保证“可见性”private volatile boolean running = true;
public void run() {
while (running) {
// 执行任务
}
}✅ 作用:
i++ 还是不安全)java.util.concurrent 包:并发工具全家桶AtomicInteger:原子类private AtomicInteger i = new AtomicInteger(0);
i.incrementAndGet(); // 原子性 i++ReentrantLock:显式锁private ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
i++;
} finally {
lock.unlock();
}✅ 比
synchronized更灵活:可尝试锁、可中断、公平锁。
ConcurrentHashMap:高并发 HashMapCopyOnWriteArrayList:读多写少场景BlockingQueue:生产者-消费者模型BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
queue.put("item"); // 队列满时阻塞
String item = queue.take(); // 队列空时阻塞// 用 BlockingQueue 最简单!
BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
// 生产者
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
queue.put("产品" + i);
System.out.println("生产:" + "产品" + i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
// 消费者
new Thread(() -> {
while (true) {
try {
String product = queue.take();
System.out.println("消费:" + product);
if (product.equals("产品10")) break;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();✅ BlockingQueue 内置了线程通信机制,是首选方案!
synchronized 和 ReentrantLock 有什么区别?区别 | synchronized | ReentrantLock |
|---|---|---|
实现 | JVM 内置锁(Monitor) | JDK 代码实现 |
释放 | 自动(方法结束或异常) | 必须手动 unlock() |
灵活性 | 低 | 高(可尝试、可中断、公平锁) |
性能 | JDK 1.6+ 已优化,差距不大 | 略高(竞争激烈时) |
💬 高分回答: “
synchronized简单,自动释放,适合大多数场景;ReentrantLock更灵活,比如我们做秒杀系统时,用tryLock(timeout)避免无限等待。”
volatile 能保证原子性吗?答: 不能!
volatile只保证可见性和有序性,不保证原子性。 比如i++操作,即使i是volatile,也可能出错。 要保证原子性,要用synchronized或AtomicInteger。
new ThreadPoolExecutor(
5, // corePoolSize:核心线程数
10, // maximumPoolSize:最大线程数
60L, // keepAliveTime:空闲线程存活时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // workQueue:任务队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);✅ 拒绝策略:
AbortPolicy:抛异常(默认)CallerRunsPolicy:由调用者线程执行DiscardPolicy:静默丢弃DiscardOldestPolicy:丢弃队列最老任务答: CPU 从一个线程切换到另一个线程执行的过程。 它涉及保存/恢复寄存器、缓存失效,开销大。 减少方法:
volatile 或 CAS 减少锁使用问题 | 答案 |
|---|---|
创建线程 | 用线程池(ExecutorService) |
线程安全 | synchronized、volatile、Atomic、ReentrantLock |
通信 | BlockingQueue > wait/notify > Condition |
集合 | ConcurrentHashMap、CopyOnWriteArrayList |
怎么说 | “我用线程池管理任务,ConcurrentHashMap 存缓存,AtomicInteger 做计数” |
多线程不是“炫技”,而是“高并发系统的发动机”。 从 Tomcat 处理请求,到 Redis 处理命令,再到 Kafka 消费消息, 背后都是多线程在高效协作。 掌握它,你才能写出高性能、线程安全的程序!
希望这篇能帮你彻底搞懂 Java 多线程!