在网上混迹多年,我的ID叫“尘民1024”。“尘民”取自《灵笼·灯塔》,而“1024”是程序员心中的无限可能。祝愿大家都能从“尘民”走向“上民”。
线程通信,通信这个词我们通常会理解为发消息,其实,线程通信 ≠ 网络通信,不是“发消息”,而是多个线程之间协调工作、交换信息的过程,目的是解决线程间的协作问题(如避免资源竞争、实现任务配合等)。
一、先搞懂:为什么线程需要通信?
在单线程程序中,代码是按顺序一步步执行的,不存在“协作”问题。但在多线程程序中,情况就完全不同了:
- 线程的执行顺序是由 CPU 调度决定的,不可预测、不可控制。
- 多个线程可能会同时访问和修改同一份共享数据。
如果没有通信机制来保证有序协作,通常会引发以下典型问题:
-
数据不一致(资源竞争)
例如两个线程同时对共享变量count执行count++操作,由于自增不是原子操作,就可能导致最终结果小于预期。public class InventoryRaceDemo { private static int stock = 100000; // 商品库存 public static void main(String[] args) throws InterruptedException { Runnable purchaseTask = () -> { for (int i = 0; i < 50000; i++) { stock--; // 非原子操作,库存可能出错 } }; Thread user1 = new Thread(purchaseTask); Thread user2 = new Thread(purchaseTask); user1.start(); user2.start(); user1.join(); user2.join(); System.out.println("剩余库存: " + stock); // 预期库存 0,但可能出现负数或库存不准确,测试剩余库存: 38826 } }说明:
stock--不是原子操作,导致“丢失更新”,可能卖出超过实际库存。。 -
线程协作失控
假设一个线程负责生产数据,另一个线程负责消费数据,如果没有通信机制,消费者可能在生产者还没准备好数据时就开始消费,导致“空取”;或者生产者疯狂生产,消费者没来得及处理,导致“堆积”。public class OrderQueueDemo { private static final Queue<String> orderQueue = new LinkedList<>(); private static final int CAPACITY = 5; public static void main(String[] args) { Thread orderProducer = new Thread(() -> { int orderId = 1; while (true) { if (orderQueue.size() < CAPACITY) { String order = "订单#" + orderId++; orderQueue.add(order); System.out.println("生成订单: " + order); } } }); Thread orderConsumer = new Thread(() -> { while (true) { if (!orderQueue.isEmpty()) { String order = orderQueue.poll(); System.out.println("处理订单: " + order); } } }); orderProducer.start(); orderConsumer.start(); } }生成订单: 订单#1 生成订单: 订单#2 生成订单: 订单#3 生成订单: 订单#4 生成订单: 订单#5分析:
-
队列已经满(达到 CAPACITY = 5)
-
生产者停止生成新订单
-
消费者尚未消费
-
出现堆积现象:生产者生成了订单,但消费者还没来得及消费,队列堆满。
说明:没有通信机制,生产者和消费者无法协调,出现堆积问题。
-
-
死锁/饥饿问题
多个线程互相等待对方释放资源,如果没有合理的通信手段协调,就可能造成所有线程都无法继续执行(死锁);或者部分线程长期得不到执行机会(饥饿)。public class EcommerceDeadlockDemo { private static final Object orderLock = new Object(); private static final Object inventoryLock = new Object(); public static void main(String[] args) { Thread t1 = new Thread(() -> { synchronized (orderLock) { System.out.println("线程1 获取订单锁"); try { Thread.sleep(100); } catch (InterruptedException e) {} synchronized (inventoryLock) { System.out.println("线程1 获取库存锁"); } } }); Thread t2 = new Thread(() -> { synchronized (inventoryLock) { System.out.println("线程2 获取库存锁"); try { Thread.sleep(100); } catch (InterruptedException e) {} synchronized (orderLock) { System.out.println("线程2 获取订单锁"); } } }); t1.start(); t2.start(); } }说明:线程1等待库存锁,线程2等待订单锁,导致死锁。
-
状态不可见(内存可见性问题)
由于 CPU 缓存和编译优化的存在,一个线程修改了共享变量,另一个线程不一定能立即看到最新值,从而导致逻辑错误。public class SeckillVisibilityDemo { private static boolean open = true; // 秒杀是否开启 public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { while (open) { // 模拟秒杀任务 } System.out.println("秒杀任务结束"); }); worker.start(); Thread.sleep(1000); open = false; // 主线程关闭秒杀 System.out.println("主线程修改 open=false"); } }说明:worker 线程可能看不到 open=false,导致秒杀任务继续执行。
👉 因此,多线程编程离不开**通信**。通信机制的核心目标是:
- 保证数据一致性(不会因为竞争导致错误结果)
- 协调执行顺序(让线程按约定的逻辑配合工作)
- 提升系统健壮性(避免死锁、饥饿、乱序等问题)
二、Java 中的线程通信方式
| 分类 | 典型实现 | 通信方向 | 适用场景 |
|---|---|---|---|
| 基础锁通信 | synchronized + wait()/notify() | 任意线程间双向通知 | 简单同步/等待-唤醒场景 |
| 条件锁通信 | Lock + Condition | 任意线程间精确通知 | 多条件判断 / 精细控制 |
| 阻塞队列通信 | BlockingQueue | 生产者 → 消费者 | 生产者-消费者模型 |
| 信号量通信 | Semaphore | 任意线程获取/释放许可 | 限流 / 资源池管理 |
| 同步屏障通信(一次性) | CountDownLatch | 子线程 → 主线程 | 启动等待 / 结果汇总 |
| 同步屏障通信(可复用) | CyclicBarrier | 多线程 ↔ 多线程 | 分阶段任务协同 |
| 轻量级状态共享 | volatile 变量 | 单向状态通知 | 开关控制 / 标志位 |
| 任务控制 | Thread.interrupt() | 请求线程 → 执行线程 | 任务取消 / 超时处理 |
| 线程等待 | join() | 主线程 ← 子线程 | 等待子线程执行结束 |
1. 基础锁通信
典型实现
wait()/notify()/notifyAll()
适用场景
- 简单的线程间同步、通知。
示例代码
public class OrderProcessingDemo {
private static final Queue<String> orderQueue = new LinkedList<>();
private static final int MAX_CAPACITY = 2;
public static void main(String[] args) {
Object lock = new Object();
Thread orderProducer = new Thread(() -> {
int orderId = 1;
while (true) {
synchronized (lock) {
while (orderQueue.size() >= MAX_CAPACITY) {
try {
lock.wait(); // 队列满,等待订单处理
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String order = "订单#" + orderId++;
orderQueue.add(order);
System.out.println("生成订单: " + order);
lock.notifyAll(); // 通知处理线程
}
}
});
Thread orderConsumer = new Thread(() -> {
while (true) {
synchronized (lock) {
while (orderQueue.isEmpty()) {
try {
lock.wait(); // 队列空,等待新订单
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String order = orderQueue.poll();
System.out.println("处理订单: " + order);
lock.notifyAll(); // 通知生产线程
}
}
});
orderProducer.start();
orderConsumer.start();
}
}
2. 条件锁通信
典型实现
Lock+Condition
适用场景
- 多条件精确通知,例如生产者-消费者模型中不同条件下的等待。
示例代码
public class InventoryOrderDemo {
private static final Queue<String> orderQueue = new LinkedList<>();
private static final int MAX_CAPACITY = 1;
private static final Lock lock = new ReentrantLock();
private static final Condition notFull = lock.newCondition();
private static final Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
Thread producer = new Thread(() -> {
int orderId = 1;
while (true) {
lock.lock();
try {
while (orderQueue.size() >= MAX_CAPACITY) {
notFull.await(); // 队列满,等待处理
}
String order = "订单#" + orderId++;
orderQueue.add(order);
System.out.println("新订单生成: " + order);
notEmpty.signal(); // 唤醒处理线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread consumer = new Thread(() -> {
while (true) {
lock.lock();
try {
while (orderQueue.isEmpty()) {
notEmpty.await(); // 队列空,等待新订单
}
String order = orderQueue.poll();
System.out.println("处理订单: " + order);
notFull.signal(); // 唤醒生产线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
producer.start();
consumer.start();
}
}
3. 阻塞队列通信
典型实现
BlockingQueue
适用场景
- 生产者-消费者模型。
示例代码
public class PaymentQueueDemo {
private final BlockingQueue<String> paymentQueue = new ArrayBlockingQueue<>(5);
public void producePayment() throws InterruptedException {
for (int i = 1; i <= 5; i++) {
String payment = "支付订单#" + i;
System.out.println("生成支付请求:" + payment);
paymentQueue.put(payment);
}
}
public void processPayment() throws InterruptedException {
while (true) {
String payment = paymentQueue.take();
System.out.println("处理支付:" + payment);
}
}
public static void main(String[] args) {
PaymentQueueDemo demo = new PaymentQueueDemo();
new Thread(() -> {
try {
demo.processPayment();
} catch (Exception e) {
}
}).start();
new Thread(() -> {
try {
demo.producePayment();
} catch (Exception e) {
}
}).start();
}
}
4. 信号量通信
典型实现
Semaphore
适用场景
- 控制资源访问许可数量,如限流、连接池。
示例代码
public class SeckillDemo {
private final Semaphore semaphore = new Semaphore(3);
public void attemptPurchase(int userId) throws InterruptedException {
semaphore.acquire();
try {
System.out.println("用户 " + userId + " 抢购成功,处理中...");
Thread.sleep(1000); // 模拟处理时间
} finally {
System.out.println("用户 " + userId + " 完成抢购,释放名额");
semaphore.release();
}
}
public static void main(String[] args) {
SeckillDemo demo = new SeckillDemo();
for (int i = 1; i <= 6; i++) {
int userId = i;
new Thread(() -> {
try {
demo.attemptPurchase(userId);
} catch (Exception e) {
}
}).start();
}
}
}
5. 同步屏障通信
典型实现
CountDownLatchCyclicBarrier
适用场景
CountDownLatch:主线程等待子任务完成。CyclicBarrier:多个线程分阶段协作。
示例代码
public class OrderBatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 1; i <= 3; i++) {
int taskId = i;
new Thread(() -> {
System.out.println("订单拆分任务 " + taskId + " 完成");
latch.countDown();
}).start();
}
latch.await();
System.out.println("所有订单拆分完成,开始统一发货");
}
}
public class DeliveryBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有配送任务到达,开始装车发货"));
for (int i = 1; i <= 3; i++) {
int deliveryId = i;
new Thread(() -> {
try {
System.out.println("配送任务 " + deliveryId + " 到达仓库");
barrier.await();
System.out.println("配送任务 " + deliveryId + " 开始配送");
} catch (Exception e) {}
}).start();
}
}
}
6. 状态变量通信 典型实现
- 典型实现:
volatile - 适用场景:单向状态通知,如开关标志。
- 示例代码:
public class SeckillSwitchDemo {
private volatile boolean open = true;
public void start() {
new Thread(() -> {
while (open) {
// 模拟秒杀任务
}
System.out.println("秒杀结束");
}).start();
}
public void stop() {
open = false;
}
public static void main(String[] args) throws InterruptedException {
SeckillSwitchDemo demo = new SeckillSwitchDemo();
demo.start();
Thread.sleep(1000);
demo.stop();
}
}
7. 任务中断通信
典型实现: Thread.interrupt()
适用场景: 任务取消、超时控制。
示例代码:
public class OrderTimeoutDemo {
public static void main(String[] args) throws InterruptedException {
Thread orderWorker = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("订单处理中...");
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("订单处理超时,中断取消");
}
});
orderWorker.start();
Thread.sleep(2000);
orderWorker.interrupt();
}
}
三、总结:怎么理解 Java 的线程通信?
-
底层依赖:共享内存模型 (JMM)
- Java 内存模型 (JMM) 保证了
volatile、锁等关键字的可见性、有序性、原子性。
- Java 内存模型 (JMM) 保证了
-
通信 ≠ 消息传递,而是“可见性 + 协作”
- 通信手段本质上是保证:
- 线程 A 的写操作 对线程 B 可见;
- 线程 A 能 通知 线程 B:某个条件成立了。
- 通信手段本质上是保证:
-
选型思路:根据业务场景选择合适机制
- 简单同步 →
synchronized + wait/notify - 复杂条件 →
Lock + Condition - 任务协作 →
CountDownLatch/CyclicBarrier - 生产消费 →
BlockingQueue - 状态通知 →
volatile/interrupt
- 简单同步 →
一句话记忆:
Java 线程通信,本质是 通过共享内存可见性 + 各种同步工具类,
实现线程间的 通知、等待、协作。