在 Java 中,线程间通信(Inter-Thread Communication,简称 ITC)是指多个线程之间如何安全地交换数据、传递信号或协调执行顺序。
Java 提供了多种线程通信机制,从最基础的共享内存方式到高级并发工具,几乎覆盖了所有常见场景。下面按实用性和使用频率从高到低系统梳理,并附上核心代码示例和注意事项。
1. 共享内存 + 同步机制(最基础、最常用)
线程共享进程的堆内存,通过共享变量进行通信,但必须保证可见性和原子性。
| 方式 | 可见性保证 | 原子性保证 | 适用场景 | 推荐程度 |
|---|---|---|---|---|
| volatile 变量 | 有(内存屏障) | 无(仅单次读写) | 状态标志、开关、单次发布 | ★★★★★ |
| synchronized | 有 | 有(锁) | 简单同步块、保护共享变量 | ★★★★ |
| AtomicXXX(CAS) | 有 | 有 | 计数器、引用更新、标志位 | ★★★★★ |
| Lock + Condition | 有 | 有 | 需要显式条件等待/通知的场景 | ★★★★ |
示例 — volatile + AtomicBoolean 实现开关
class SharedFlag {
private volatile boolean running = true; // 可见性
private final AtomicBoolean stopped = new AtomicBoolean(false);
public void stop() {
stopped.set(true);
running = false;
}
public boolean isRunning() {
return running && !stopped.get();
}
}
2. 等待/通知机制(经典生产者-消费者模式核心)
这是 Java 最传统、最重要的线程协作方式,基于 Object 的监视器(monitor)。
核心方法(必须在 synchronized 块/方法中使用):
wait()/wait(long timeout):释放锁,进入等待队列notify():随机唤醒一个等待线程notifyAll():唤醒所有等待线程
经典生产者-消费者示例(使用 wait/notify)
class MessageBox {
private String message;
private boolean empty = true;
public synchronized String take() {
while (empty) { // 防止虚假唤醒,必须用 while
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
String msg = message;
empty = true;
notifyAll(); // 通知生产者可以继续生产
return msg;
}
public synchronized void put(String msg) {
while (!empty) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
message = msg;
empty = false;
notifyAll(); // 通知消费者可以消费
}
}
现代替代方案:java.util.concurrent.locks.Condition
class MessageBoxModern {
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private String message;
private boolean empty = true;
public String take() throws InterruptedException {
lock.lock();
try {
while (empty) {
notEmpty.await();
}
String msg = message;
empty = true;
notFull.signalAll();
return msg;
} finally {
lock.unlock();
}
}
public void put(String msg) throws InterruptedException {
lock.lock();
try {
while (!empty) {
notFull.await();
}
message = msg;
empty = false;
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
}
为什么 Condition 更好?
- 可以有多个等待队列(多个 Condition)
- 支持 interruptible、timed await
- 更灵活、更清晰
3. 高级并发工具类(JUC 包)—— 推荐在现代项目中使用
| 工具类 | 通信方式 | 典型场景 | 线程安全 |
|---|---|---|---|
| BlockingQueue(ArrayBlockingQueue, LinkedBlockingQueue) | put / take | 生产者-消费者、线程池任务队列 | 是 |
| SynchronousQueue | 直接交接 | 无缓冲的线程间直接传递 | 是 |
| Exchanger | 两线程交换数据 | 两个线程互相交换数据(如并行排序) | 是 |
| CyclicBarrier / CountDownLatch | 线程等待计数或屏障 | 多线程协作完成阶段性任务 | 是 |
| Phaser | 更灵活的分阶段屏障 | 动态参与者、阶段性任务 | 是 |
| CompletionService / Future | 任务结果异步获取 | 批量任务结果收集 | 是 |
最常用示例 — BlockingQueue 实现生产者消费者
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
Producer: queue.put("data"); // 队列满则阻塞
Consumer: String data = queue.take(); // 队列空则阻塞
4. 其他通信方式(了解即可)
- PipedInputStream / PipedOutputStream:线程间管道流(字节流)
- join():一个线程等待另一个线程结束
- Thread.interrupt() + isInterrupted():中断信号通信
- 共享 volatile 数组 / 对象 + 自定义协议
- LockSupport.park() / unpark():更底层的线程挂起/唤醒(Unsafe 级别)
5. 虚拟线程(Java 21+)下的线程通信(特别说明)
虚拟线程本质上是轻量级用户态线程,通信方式与平台线程完全相同:
- 仍然使用
wait/notify、Condition、BlockingQueue等 - 阻塞操作(如
queue.take()、Thread.sleep()、Lock.lock())不会阻塞 carrier 线程(ForkJoinPool 的工作线程) - 推荐:优先使用
BlockingQueue、CompletableFuture、StructuredTaskScope(Java 21+)来组织协作,更符合结构化并发思想
虚拟线程时代推荐写法示例(Java 21+):
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var scope = new StructuredTaskScope.ShutdownOnFailure();
var task1 = scope.fork(() -> fetchDataFromDb());
var task2 = scope.fork(() -> fetchDataFromApi());
scope.join(); // 等待所有子任务完成
scope.throwIfFailed();
// 合并结果
process(task1.get(), task2.get());
}
总结:线程间通信方式选择指南(2025–2026 推荐)
| 场景 | 首选方式 | 次选方式 |
|---|---|---|
| 简单状态标志、开关 | volatile / AtomicBoolean | — |
| 生产者-消费者模型 | BlockingQueue(Linked / Array) | wait/notify 或 Condition |
| 需要多个条件等待 | ReentrantLock + 多 Condition | — |
| 两个线程交换一次数据 | Exchanger | — |
| 多线程等待某个条件完成 | CountDownLatch / CyclicBarrier | Phaser |
| 任务结果异步收集 | CompletableFuture / CompletionService | — |
| 虚拟线程高并发协作 | StructuredTaskScope + virtual executor | BlockingQueue |
一句话总结:
Java 线程间通信的核心是 “共享 + 同步” 或 “消息传递”,
现代项目中 优先使用 JUC 工具类(尤其是 BlockingQueue 和 StructuredTaskScope),能极大降低错误率并提高可读性。
如果你有具体场景(生产者消费者、线程池任务协调、虚拟线程下通信等),我可以给出更针对性的完整代码实现。