Java线程通信详解
发布时间:2025-06-30 00:35       
以下是Java线程通信的全面解析,基于Java 17 LTS版本的最佳实践,涵盖核心机制与实战要点:
一、线程通信的本质
多线程共享进程内存空间,通信即安全地交换数据与协调执行顺序。需解决两大核心问题:
- 竞态条件(非原子操作导致数据不一致)
- 可见性(线程缓存未及时刷新到主内存)
⚠️ 错误示例:
// 危险操作:多个线程同时修改共享变量 public class Counter { private int count = 0; public void increment() { count++; } // 非原子操作 }
二、8种线程通信机制详解
1. 同步锁(synchronized)
- 原理:基于JVM内置锁(Monitor),保证代码块互斥执行
- 适用场景:简单的临界区保护
public class SafeCounter {
private int count = 0;
public synchronized void increment() {
count++; // 原子操作
}
}
2. wait()/notify() 机制
- 核心要点:
- 必须在
synchronized块内调用 wait()释放锁并进入等待notify()唤醒单个等待线程
- 必须在
- 经典生产者-消费者模型:
public class Buffer {
private Queue<Integer> queue = new LinkedList<>();
private int capacity = 10;
public synchronized void produce(int value) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // 缓冲区满时等待
}
queue.add(value);
notifyAll(); // 唤醒消费者
}
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 缓冲区空时等待
}
int value = queue.poll();
notifyAll(); // 唤醒生产者
return value;
}
}
3. Lock 框架(java.util.concurrent.locks)
- 优势:比
synchronized更灵活(可中断、超时、公平锁) - ReentrantLock 示例:
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public void produce(int value) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 指定条件等待
}
queue.add(value);
notEmpty.signal(); // 精准唤醒消费者
} finally {
lock.unlock();
}
}
4. 阻塞队列(BlockingQueue)
- 最佳实践:直接使用线程安全容器
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// 生产者
queue.put(1); // 阻塞直到空间可用
// 消费者
int value = queue.take(); // 阻塞直到元素可用
5. 信号量(Semaphore)
- 场景:控制并发资源数量
Semaphore semaphore = new Semaphore(5); // 允许5个线程同时访问
void accessResource() throws InterruptedException {
semaphore.acquire(); // 获取许可
try {
// 操作共享资源
} finally {
semaphore.release(); // 释放许可
}
}
6. 栅栏(CyclicBarrier)
- 场景:多阶段任务同步
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("所有线程到达屏障点"));
// 工作线程
public void run() {
// 阶段1工作
barrier.await(); // 等待其他线程
// 阶段2工作
}
7. 倒计时锁(CountDownLatch)
- 一次性场景:主线程等待所有子线程完成
CountDownLatch latch = new CountDownLatch(3);
// 工作线程
public void run() {
// ... 执行任务
latch.countDown(); // 计数器减1
}
// 主线程
latch.await(); // 阻塞直到计数器归零
8. 异步结果(Future & CompletableFuture)
- Java 8+ 推荐:链式异步编程
CompletableFuture.supplyAsync(() -> fetchData())
.thenApply(data -> processData(data))
.thenAccept(result -> saveResult(result))
.exceptionally(ex -> handleError(ex));
三、内存可见性解决方案
1. volatile 关键字
- 作用:保证变量的可见性(不保证原子性)
- 适用场景:单写多读状态标志
private volatile boolean running = true;
public void stop() { running = false; } // 写操作
public void run() {
while (running) { /* 读操作 */ }
}
2. Happens-Before 原则
JVM规定的内存可见性保证(如锁释放先于后续获取)
四、通信机制对比表
| 机制 | 特点 | 适用场景 |
|---|---|---|
synchronized |
JVM内置,简单可靠 | 简单的临界区保护 |
|