Java高频面试之并发编程-07
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶
面试官:线程之间有哪些通信方式?
在 Java 多线程编程中,线程间通信(Inter-Thread Communication)的核心目的是协调不同线程的执行顺序或共享数据。以下是线程间通信的常见方式及其实现原理和应用场景:
1. 共享内存 + 同步机制
原理:线程通过共享变量(如对象、集合)交换数据,通过锁(synchronized
、Lock
)或原子类(AtomicInteger
等)保证线程安全。
示例:
// 共享变量
private int sharedValue = 0;
private final Object lock = new Object();// 线程 A 写入
synchronized (lock) {sharedValue = 42;lock.notifyAll(); // 通知等待的线程
}// 线程 B 读取
synchronized (lock) {while (sharedValue == 0) {lock.wait(); // 等待通知}System.out.println(sharedValue);
}
适用场景:简单的数据共享,需手动处理同步和竞态条件。
2. 阻塞队列(BlockingQueue)
原理:线程通过线程安全的队列传递数据,生产者写入队列,消费者从队列读取。队列满或空时自动阻塞。
示例:
BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);// 生产者线程
queue.put("data"); // 队列满时阻塞// 消费者线程
String data = queue.take(); // 队列空时阻塞
适用场景:生产者-消费者模型,解耦生产与消费逻辑。
3. wait() / notify() / notifyAll()
原理:基于对象监视器(Monitor)的等待-通知机制,需在 synchronized
块中使用。
示例:
public class TaskCoordinator {private boolean isReady = false;public void waitForReady() throws InterruptedException {synchronized (this) {while (!isReady) {this.wait(); // 释放锁并等待}}}public void setReady() {synchronized (this) {isReady = true;this.notifyAll(); // 唤醒所有等待线程}}
}
适用场景:线程间的条件协作(如任务启动前的等待)。
4. 同步工具类(Semaphore、CountDownLatch 等)
原理:通过计数器或状态控制线程的执行流程。
常用类:
- CountDownLatch:等待多个线程完成初始化。
CountDownLatch latch = new CountDownLatch(3);// 工作线程 latch.countDown(); // 计数器减 1// 主线程 latch.await(); // 等待计数器归零
- CyclicBarrier:多线程相互等待至屏障点。
CyclicBarrier barrier = new CyclicBarrier(3);// 每个线程执行到屏障点后等待 barrier.await();
- Semaphore:控制并发线程数量。
Semaphore semaphore = new Semaphore(5);semaphore.acquire(); // 获取许可(无可用许可时阻塞) semaphore.release(); // 释放许可
适用场景:复杂线程协作(如分批任务、资源池管理)。
5. 管道(PipedInputStream / PipedOutputStream)
原理:通过管道流直接传递字节数据,需成对使用。
示例:
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
pis.connect(pos); // 连接输入输出流// 生产者线程写入数据
pos.write("data".getBytes());// 消费者线程读取数据
int data = pis.read();
适用场景:线程间直接传输字节数据(较少使用,性能较低)。
6. Future 和 Callable
原理:通过 Future
获取另一个线程的异步执行结果。
示例:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {Thread.sleep(1000);return 42;
});// 阻塞等待结果
int result = future.get();
适用场景:异步任务执行与结果获取。
7. 事件驱动(如 EventBus)
原理:基于发布-订阅模式,线程通过事件总线传递消息。
示例(使用 Guava EventBus):
EventBus eventBus = new EventBus();// 订阅者
class Subscriber {@Subscribepublic void handleEvent(String event) {System.out.println("Received: " + event);}
}eventBus.register(new Subscriber());// 发布者线程
eventBus.post("Hello");
适用场景:松耦合的事件通知(如 GUI 事件处理)。
8. 回调(Callback)
原理:线程 A 调用线程 B 的方法时传入回调函数,线程 B 完成任务后调用回调函数通知线程 A。
示例:
interface Callback {void onComplete(String result);
}class Worker {void doWork(Callback callback) {new Thread(() -> {String result = "Done";callback.onComplete(result);}).start();}
}// 调用
new Worker().doWork(result -> System.out.println(result));
适用场景:异步任务完成后的通知。
对比与选型建议
通信方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
共享内存 + 同步 | 简单直接 | 需手动处理同步,易出错 | 简单数据共享 |
阻塞队列 | 线程安全,解耦生产消费 | 队列容量需合理设置 | 生产者-消费者模型 |
wait/notify | 灵活的条件控制 | 需搭配 synchronized 使用 | 线程条件协作 |
同步工具类 | 简化复杂协作逻辑 | 需理解不同工具的特性 | 多线程分阶段协作 |
Future/Callable | 支持异步结果获取 | 仅适用于单次任务 | 异步任务执行 |
事件驱动 | 松耦合,易扩展 | 依赖第三方库(如 EventBus) | 事件通知场景 |
回调 | 灵活定制逻辑 | 回调嵌套可能导致“回调地狱” | 异步任务完成通知 |
注意事项
- 线程安全:共享变量必须通过锁或原子类确保可见性和原子性。
- 死锁预防:避免嵌套锁和循环等待资源。
- 性能权衡:高并发场景优先选择无锁或细粒度锁(如
ConcurrentHashMap
)。 - 资源释放:确保
wait()
后能正常唤醒,避免线程永久阻塞。