线程间的通信方式
·
一、共享内存(基础方式:synchronized / 显式锁)
核心原理
多个线程访问同一个内存区域(成员变量、静态变量),通过 synchronized(内置锁)或 Lock(显式锁)保证「同一时间只有一个线程读写共享数据」,避免竞态条件(数据错乱)。这是 Java 最基础的线程通信方式。
实现示例 1:synchronized 实现共享计数器
public class SharedMemoryDemo {
// 共享变量
private static int sharedCounter = 0;
// 同步方法:内置锁保护共享变量
private static synchronized void increment() {
sharedCounter++;
}
private static synchronized void decrement() {
sharedCounter--;
}
public static void main(String[] args) throws InterruptedException {
// 线程1:累加
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
increment();
}
});
// 线程2:递减
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
decrement();
}
});
t1.start();
t2.start();
// 等待线程执行完毕
t1.join();
t2.join();
// 无锁会随机出错,加锁后结果必为0
System.out.println("最终共享变量值:" + sharedCounter);
}
}
实现示例 2:Lock 显式锁(更灵活)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockSharedMemoryDemo {
private static int sharedCounter = 0;
// 显式锁(可重入锁)
private static final Lock lock = new ReentrantLock();
private static void increment() {
lock.lock(); // 获取锁
try {
sharedCounter++;
} finally {
lock.unlock(); // 必须在finally释放锁,避免死锁
}
}
private static void decrement() {
lock.lock();
try {
sharedCounter--;
} finally {
lock.unlock();
}
}
// main方法和示例1一致,此处省略
}
适用场景
- 简单数据共享(计数器、状态标记:
isRunning = true/false); - 低频率、短时间的临界区操作。
优缺点
- ✅ 优点:实现简单、无额外依赖,JVM 原生支持;
- ❌ 缺点:仅能保证数据安全,无法主动「唤醒 / 等待」线程,需配合其他方式实现复杂协作。
二、wait ()/notify ()/notifyAll ()(基于 Object 监视器)
核心原理
这是 Java 最经典的线程协作方式,依赖 Object 类的内置监视器(必须在 synchronized 块中使用):
wait():释放当前持有的锁,让线程进入「等待队列」,直到被唤醒;notify():随机唤醒一个等待该对象锁的线程;notifyAll():唤醒所有等待该对象锁的线程(推荐优先使用,避免线程饿死)。
实现示例:生产者 - 消费者模型
public class WaitNotifyDemo {
// 共享缓冲区
private static final Object lock = new Object();
private static int count = 0; // 缓冲区数据量
private static final int MAX_CAPACITY = 5; // 缓冲区最大容量
// 生产者线程:生产数据
static class Producer implements Runnable {
@Override
public void run() {
while (true) {
synchronized (lock) {
// 缓冲区满,等待消费者消费
while (count == MAX_CAPACITY) {
try {
lock.wait(); // 释放锁,进入等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 生产数据
count++;
System.out.println(Thread.currentThread().getName() + " 生产,当前容量:" + count);
lock.notifyAll(); // 唤醒消费者
}
// 模拟生产耗时
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
// 消费者线程:消费数据
static class Consumer implements Runnable {
@Override
public void run() {
while (true) {
synchronized (lock) {
// 缓冲区空,等待生产者生产
while (count == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 消费数据
count--;
System.out.println(Thread.currentThread().getName() + " 消费,当前容量:" + count);
lock.notifyAll(); // 唤醒生产者
}
// 模拟消费耗时
try {
Thread.sleep(800);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public static void main(String[] args) {
new Thread(new Producer(), "生产者1").start();
new Thread(new Consumer(), "消费者1").start();
}
}
关键注意事项
- 必须在
synchronized块中调用wait()/notify(),否则抛IllegalMonitorStateException; wait()会释放锁,而sleep()不会释放锁;- 推荐用
while循环判断条件(而非if),防止「虚假唤醒」(线程被唤醒后条件仍不满足)。
适用场景
- 经典的生产者 - 消费者、生产者 - 多个消费者等协作场景;
- 需要手动控制线程等待 / 唤醒的基础场景。
优缺点
- ✅ 优点:JVM 原生支持,无需额外依赖;
- ❌ 缺点:唤醒线程随机(notify)、只能唤醒同一锁的线程、代码嵌套较复杂。
三、Condition(基于 Lock 的灵活协作)
核心原理
Condition 是 Lock 的配套工具(替代 wait()/notify()),通过 Lock.newCondition() 创建,支持「分组唤醒」(精准唤醒指定线程),比 wait/notify 更灵活。
实现示例:精准唤醒生产者 / 消费者
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private static int count = 0;
private static final int MAX_CAPACITY = 5;
private static final Lock lock = new ReentrantLock();
// 分别创建生产者、消费者的Condition
private static final Condition producerCondition = lock.newCondition();
private static final Condition consumerCondition = lock.newCondition();
static class Producer implements Runnable {
@Override
public void run() {
while (true) {
lock.lock();
try {
while (count == MAX_CAPACITY) {
producerCondition.await(); // 生产者等待
}
count++;
System.out.println(Thread.currentThread().getName() + " 生产,当前容量:" + count);
consumerCondition.signalAll(); // 只唤醒消费者
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
static class Consumer implements Runnable {
@Override
public void run() {
while (true) {
lock.lock();
try {
while (count == 0) {
consumerCondition.await(); // 消费者等待
}
count--;
System.out.println(Thread.currentThread().getName() + " 消费,当前容量:" + count);
producerCondition.signalAll(); // 只唤醒生产者
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
try {
Thread.sleep(800);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public static void main(String[] args) {
new Thread(new Producer(), "生产者1").start();
new Thread(new Consumer(), "消费者1").start();
}
}
适用场景
- 需要「精准唤醒」指定类型线程的场景(如多生产者多消费者);
- 复杂的线程协作(多个条件等待 / 唤醒)。
优缺点
- ✅ 优点:分组唤醒、灵活可控,支持超时等待;
- ❌ 缺点:代码稍复杂,需手动管理 Lock 和 Condition。
四、BlockingQueue(阻塞队列:高并发首选)
核心原理
BlockingQueue 是 Java 并发包提供的「线程安全阻塞队列」,内置了 put()(队列满则阻塞)、take()(队列空则阻塞)等方法,无需手动加锁 / 唤醒,是生产者 - 消费者模型的最优解。
实现示例:简化版生产者 - 消费者
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueDemo {
// 容量为5的阻塞队列
private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
static class Producer implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
try {
queue.put(i); // 队列满则阻塞
System.out.println(Thread.currentThread().getName() + " 生产:" + i);
i++;
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
static class Consumer implements Runnable {
@Override
public void run() {
while (true) {
try {
Integer data = queue.take(); // 队列空则阻塞
System.out.println(Thread.currentThread().getName() + " 消费:" + data);
Thread.sleep(800);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public static void main(String[] args) {
new Thread(new Producer(), "生产者1").start();
new Thread(new Consumer(), "消费者1").start();
}
}
常用 BlockingQueue 实现
| 实现类 | 特点 | 适用场景 |
|---|---|---|
| ArrayBlockingQueue | 数组实现、有界、公平 / 非公平 | 固定容量的生产消费 |
| LinkedBlockingQueue | 链表实现、默认无界 | 高吞吐量的生产消费 |
| SynchronousQueue | 无存储、直接传递 | 线程间一对一数据交换 |
适用场景
- 高并发的生产者 - 消费者模型;
- 线程间安全的消息传递(如任务队列)。
优缺点
- ✅ 优点:无需手动处理锁和唤醒、代码简洁、性能优异;
- ❌ 缺点:仅适用于队列式数据交换,灵活性稍低。
五、并发工具类(CountDownLatch/CyclicBarrier/Semaphore)
这类工具类并非「直接通信」,而是通过「协调线程执行时机」实现间接通信,是 Java 高级线程协作方式。
1. CountDownLatch(倒计时门闩)
- 原理:让一个 / 多个线程等待其他线程完成操作后再执行;
- 示例:主线程等待 3 个工作线程完成任务:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 计数3
// 工作线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
latch.countDown(); // 计数减1
}, "工作线程" + i).start();
}
latch.await(); // 主线程等待计数为0
System.out.println("所有工作线程完成,主线程继续执行");
}
}
2. CyclicBarrier(循环屏障)
- 原理:让多个线程到达「屏障点」后再一起执行(可循环使用);
- 适用场景:多线程分批执行任务(如分阶段计算)。
3. Semaphore(信号量)
- 原理:控制同时访问某个资源的线程数(通过许可数实现);
- 适用场景:限流(如最多允许 5 个线程访问数据库连接池)。
总结
关键点回顾
- 基础通信:共享内存(synchronized/Lock)保证数据安全,但需配合 wait/notify 实现协作;
- 经典协作:wait/notify(Object 监视器)是基础,Condition(Lock 配套)更灵活,支持精准唤醒;
- 高并发首选:BlockingQueue 无需手动处理锁和唤醒,是生产者 - 消费者模型的最优解;
- 高级协作:CountDownLatch/CyclicBarrier/Semaphore 用于协调线程执行时机,实现间接通信。
选型建议
- 简单数据共享:用 synchronized/Lock;
- 生产消费模型:优先用 BlockingQueue;
- 精准线程唤醒:用 Condition;
- 线程执行时机协调:用 CountDownLatch/CyclicBarrier/Semaphore。
更多推荐


所有评论(0)