线程间有哪些通信方式?
未标记
考查重点:消息传递与内存同步。共享对象用锁/原子建立可见性,队列传消息并协调等待;选择时看数据所有权、顺序和是否需要阻塞通知
本题目录
参考答案
核心回答
线程通常共享同一进程的地址空间,所以线程通信既要考虑数据怎样交给对方,也要考虑对方何时能安全地使用它。以 Java 为例,常用方式有四类:
- 共享状态配合同步:通过共享对象交换数据,用
synchronized、Lock或原子变量等保证相应访问的互斥与可见性;简单状态发布也可以使用合适的volatile协议。 - 等待与通知:用
Object的wait/notify/notifyAll,或Lock的Condition,让线程在条件不满足时等待,条件变化后再继续。 - 线程安全队列传递任务或数据:例如
BlockingQueue,生产者放入任务、消费者取出处理,同时封装并发访问和必要的等待,适合生产者—消费者模式。 - 任务完成与阶段协调:用
Future获取异步结果,join等待线程结束,用CountDownLatch等待一组任务完成;多轮汇合可考虑CyclicBarrier等工具。
这些工具并非同一层次:共享对象或队列承载数据,锁保护访问,条件和完成信号协调执行。实际开发应按协作关系选择,而不是把所有机制叠在一起。
进一步解释
wait/notify 的关键规则是什么?
调用 wait、notify 或 notifyAll 时,需要持有同一对象的监视器。wait 把线程放入该对象的等待集合,并释放这个对象的监视器;它不会顺带释放线程持有的其他锁。返回前,线程还需要重新取得该监视器。
notify 只是选择一个等待线程进行唤醒,不会立即交出当前锁,也不保证被唤醒者下一刻就获得锁。notifyAll 唤醒全部等待者,仍由它们重新竞争。
等待要围绕共享条件使用 while 检查:即使收到通知,条件也可能已被其他线程改变;还可能发生虚假唤醒、超时或中断。应在同一套锁规则下检查条件、修改状态、发出通知。通知本身不保存业务结果,也不能代替条件状态。
sleep 只是暂停当前线程,并不释放已经持有的监视器,更不能保证另一个线程恰好把数据准备好。
Condition 比 wait/notify 多了什么?
Condition 与具体 Lock 关联,可以为同一把锁建立多个等待条件。例如有界缓冲区把“非空”和“非满”分开:消费者等待非空,生产者等待非满。
await 在等待时释放关联的锁,返回前重新取得锁;signal/signalAll 用于通知相应等待者。使用时同样要检查条件、处理虚假唤醒和中断,不能把 signal 理解为把锁直接移交给指定线程。
为什么生产者—消费者通常优先考虑 BlockingQueue?
参考场景是一个线程读取输入、若干工作线程处理数据。生产者把任务放进有界队列,消费者取出后处理;队列为空时 take 可以等待,队列满时 put 可以等待,带超时的 offer/poll 则允许调用方作出超时处理。
队列把数据传递与并发协调封装在一起,减少手写条件等待的错误,也能通过容量和入队策略形成背压。不过不是所有入队方法都会阻塞,offer 可能立即返回失败;无界或容量过大的队列也不能有效约束积压。
BlockingQueue 还提供发布保证:入队前的相关动作 happens-before 另一线程访问或移除该元素后的动作。但这不保护入队后仍被生产者随意修改的对象;任务交接后,最好明确对象由谁继续修改。
常见追问
只想等几个异步任务完成,也要用队列吗?
不一定。需要取得某个任务结果时,可以使用 Future;只关心一组任务是否全部完成,可以使用 CountDownLatch。它们解决完成协调,具体结果仍需通过返回值或正确同步的数据结构传递。等待还应考虑超时、异常与取消。
ThreadLocal 能用于线程之间传数据吗?
普通 ThreadLocal 用于按线程保存各自状态,不会自动把一个线程的值交给另一个线程。跨线程传递应使用明确的参数、任务包装、结果或通信结构。
线程内队列和消息中间件有什么区别?
BlockingQueue 通常只在当前进程内协调线程,不自动提供跨服务投递、进程崩溃后的持久恢复等能力。是否需要消息中间件,要根据进程边界和可靠性要求另行选择。
参考资料
- 国内:JavaGuide:Java 并发常见面试题(上):采用 sleep 与 wait 的用途、监视器关联等说明,解释暂停执行与等待其他线程改变条件之间的区别。
- 国内:JavaGuide:ArrayBlockingQueue 源码分析:采用非空、非满两个条件及 put/take 的生产者—消费者流程,说明队列怎样组合数据传递、互斥和等待通知。
- 国内:小林 coding:多线程冲突了怎么办?:采用同步与互斥的区分、缓冲区生产消费的分析,解释线程协作为什么同时需要保护数据和协调先后。
- 国外:Java SE 25:Object:核对 wait 释放与重获监视器、notify 的竞争规则以及虚假唤醒,说明条件循环和持锁要求的原因。
- 国外:Java SE 25:BlockingQueue:核对阻塞、立即返回和超时方法的区别,以及元素交接的 happens-before 保证,说明有界队列能解决什么、仍需业务处理什么。
