线程通信的核心机制
线程通信的核心机制
线程通信是多线程协作的基础,目的是让线程之间能够传递信息或协调执行顺序。Java中主要通过以下方式实现:
1. wait()
与 notify()/notifyAll()
原理
- 等待-通知机制:线程通过共享对象的监视器(锁)进行通信。
wait()
:释放锁并进入等待状态,直到其他线程调用notify()
。notify()
:随机唤醒一个优先级最高的等待线程。notifyAll()
:唤醒所有等待线程。
- 线程通信涉及上面三个方法:wait()、notify()、notifyAll();这三个方法都是Object类的方法
- 其中wait()方法重载了三个:
- wait():调用此方法,线程进入”等待状态“
- wait(毫秒):调用此方法,线程进入”超时等待状态“
- wait(毫秒,纳秒):调用此方法,线程进入”超时等待状态“
- 调用wait方法和notify相关方法,不是通过线程对象去调用,而是通过共享对象去调用
- 例如:obj.wait(),
- obj是多线程共享的对象。当调用wait方法之后,在obj对象上活跃的所有线程进入无限期等待。直到调用了该共享对象的notify方法进行了唤醒。唤醒之后,会接着上一次调用wait()方法的位置继续向下执行。
- obj.wait()方法调用之后,会释放之前占用的对象锁。
代码示例(生产者-消费者模型)
public class WaitNotifyDemo {private final Object lock = new Object();private boolean isProduced = false; // 标记是否有数据// 生产者public void produce() throws InterruptedException {synchronized (lock) {while (isProduced) { // 防止虚假唤醒lock.wait(); // 等待消费者消费}System.out.println("生产数据");isProduced = true;lock.notify(); // 通知消费者}}// 消费者public void consume() throws InterruptedException {synchronized (lock) {while (!isProduced) {lock.wait(); // 等待生产者生产}System.out.println("消费数据");isProduced = false;lock.notify(); // 通知生产者}}
}
关键点
- 必须在同步块中使用:调用
wait()
/notify()
前需持有对象锁。 - 循环检查条件:使用
while
而非if
,防止虚假唤醒。 - 单次唤醒与批量唤醒:
notify()
效率高,notifyAll()
更安全。
2. Condition
接口(显式锁的通信)
原理
- 条件队列:通过
Lock.newCondition()
创建多个条件变量,实现精准唤醒。 await()
:释放锁并等待信号,类似wait()
。signal()/signalAll()
:唤醒指定条件的线程。
代码示例
public class ConditionDemo {private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition(); // 队列未满条件private final Condition notEmpty = lock.newCondition(); // 队列非空条件private final Queue<Integer> queue = new LinkedList<>();private final int CAPACITY = 10;// 生产者public void produce(int value) throws InterruptedException {lock.lock();try {while (queue.size() == CAPACITY) {notFull.await(); // 等待队列未满}queue.add(value);notEmpty.signal(); // 唤醒消费者} finally {lock.unlock();}}// 消费者public int consume() throws InterruptedException {lock.lock();try {while (queue.isEmpty()) {notEmpty.await(); // 等待队列非空}int value = queue.poll();notFull.signal(); // 唤醒生产者return value;} finally {lock.unlock();}}
}
优势
- 多条件控制:不同条件独立唤醒(如生产者唤醒消费者,消费者唤醒生产者)。
- 灵活性:支持超时等待(
awaitNanos()
)和可中断等待。
3. 阻塞队列(BlockingQueue
)
原理
- 生产者-消费者解耦:通过队列的
put()
和take()
方法自动处理阻塞。 - 线程安全:内部已实现同步机制,无需手动加锁。
代码示例
public class BlockingQueueDemo {private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);// 生产者public void produce(int value) throws InterruptedException {queue.put(value); // 队列满时自动阻塞System.out.println("生产: " + value);}// 消费者public void consume() throws InterruptedException {int value = queue.take(); // 队列空时自动阻塞System.out.println("消费: " + value);}
}
常用实现类
ArrayBlockingQueue
:有界队列,数组实现。LinkedBlockingQueue
:可选有界或无界,链表实现。PriorityBlockingQueue
:支持优先级排序。SynchronousQueue
:不存储元素,直接传递数据。
4. 同步工具类
(1) CountDownLatch
- 一次性屏障:等待多个线程完成初始化后继续执行。
CountDownLatch latch = new CountDownLatch(3); // 子线程中 latch.countDown(); // 主线程中 latch.await();
(2) CyclicBarrier
- 可重用屏障:多线程到达屏障后同时继续执行。
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程到达屏障")); // 线程中 barrier.await();
(3) Phaser
- 灵活阶段同步:支持动态注册/注销线程,分阶段同步。
Phaser phaser = new Phaser(3); // 初始3个线程 phaser.arriveAndAwaitAdvance(); // 到达并等待其他线程
对比与选择建议
机制 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
wait/notify | 简单的等待-通知逻辑 | 无需额外依赖 | 需手动处理锁和条件判断 |
Condition | 多条件精准唤醒 | 灵活控制不同条件 | 代码复杂度较高 |
BlockingQueue | 生产者-消费者模型 | 高度封装,自动阻塞 | 队列容量需合理设置 |
同步工具类 | 多线程分阶段协同任务 | 功能强大,支持复杂场景 | 学习成本较高 |
最佳实践
- 优先使用高层工具:如
BlockingQueue
和同步工具类,减少手动同步代码。 - 避免嵌套锁:防止死锁,按固定顺序获取锁。
- 防御性编程:使用
while
循环检查条件,处理虚假唤醒。 - 资源释放:确保在
finally
块中释放锁或信号量。
常见误区
- 忘记唤醒:调用
wait()
后未在条件变化时调用notify()
,导致线程永久等待。 - 错误的条件检查:使用
if
而非while
检查条件,导致逻辑错误。 - 锁对象混淆:不同线程使用不同锁对象调用
wait()
,导致IllegalMonitorStateException
。
通过合理选择线程通信机制,可以高效实现多线程协作,构建健壮的并发程序!