线程间有哪些通信方式?

考查重点:消息传递与内存同步。共享对象用锁/原子建立可见性,队列传消息并协调等待;选择时看数据所有权、顺序和是否需要阻塞通知

本题目录

参考答案

核心回答

线程通常共享同一进程的地址空间,所以线程通信既要考虑数据怎样交给对方,也要考虑对方何时能安全地使用它。以 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 保证,说明有界队列能解决什么、仍需业务处理什么。

相关面经

5 篇

经纬恒润

数据平台岗位
一面
查看原面经 →
1个月前的面试,自动驾驶公司的数据平台岗位。问到了数仓基本做sql开发于是随便问了几个kpi问题草草结束了。也是更多是后端的题目,比如什么设计高并发系统,线程通信,另外还有场景题设计Agent检测思路。(具体面经见p1)

美团

软件开发工程师
秋招一面
查看原面经 →
县城(线程)之间是怎么通信的?

BIGO

客户端开发工程师
秋招二面
查看原面经 →
进程线程的区别,怎么调度的,怎么通信的?

哔哩哔哩(B站)

多屏客户端开发工程师
实习二面
查看原面经 →
常用的线程间通信方式有哪些

快手

后端开发工程师
一面
查看原面经 →
做线程间数据同步的话,在 Java 里面该怎么做?

收录题集

1 个来源,共1 份题集

相关题目

4 题