在 Java 多线程编程中,线程之间的通信是实现协作和同步的关键。以下是常见的线程通信方式及其实现方法:
1. 共享变量 + `wait()` / `notify()` / `notifyAll()`
这是基于 `Object` 类的经典线程通信机制,需配合 `synchronized` 同步块使用。
适用场景
生产者 - 消费者模型、线程协作任务。
示例
java
public class SharedResource {
private int value;
private boolean isEmpty = true;
public synchronized void produce(int newValue) throws InterruptedException {
while (!isEmpty) {
wait(); // 等待消费者消费
}
value = newValue;
isEmpty = false;
notifyAll(); // 通知消费者可以消费
}
public synchronized int consume() throws InterruptedException {
while (isEmpty) {
wait(); // 等待生产者生产
}
isEmpty = true;
notifyAll(); // 通知生产者可以生产
return value;
}
}
特点
* `wait()` 释放锁并让线程进入等待状态。
* `notify()` 唤醒一个等待线程,`notifyAll()` 唤醒所有等待线程。
* 必须用 `while` 循环检查条件,防止 **虚假唤醒**(spurious wakeup)。
2. `Lock` 和 `Condition`(显式锁)
Java 5 引入的 `java.util.concurrent.locks` 包提供了更灵活的锁机制,支持多个条件变量。
适用场景
复杂条件协作(如多生产者 - 多消费者)。
示例
java
import java.util.concurrent.locks.*;
public class SharedResourceWithLock {
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private int value;
private boolean isEmpty = true;
public void produce(int newValue) throws InterruptedException {
lock.lock();
try {
while (!isEmpty) {
notFull.await(); // 等待队列非满
}
value = newValue;
isEmpty = false;
notEmpty.signal(); // 通知队列非空
} finally {
lock.unlock();
}
}
public int consume() throws InterruptedException {
lock.lock();
try {
while (isEmpty) {
notEmpty.await(); // 等待队列非空
}
isEmpty = true;
notFull.signal(); // 通知队列非满
return value;
} finally {
lock.unlock();
}
}
}
特点
* 可以创建多个 `Condition` 对象,精确控制线程的等待和唤醒。
* 支持公平锁、非公平锁,以及 `tryLock()` 等灵活操作。
3. 阻塞队列(`BlockingQueue`)
`java.util.concurrent.BlockingQueue` 提供了线程安全的队列操作,天然支持线程通信。
适用场景
生产者 - 消费者模型的简化实现。
示例
java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// 生产者
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
queue.put(i); // 队列满时自动阻塞
System.out.println("Produced: " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 消费者
new Thread(() -> {
try {
while (true) {
int value = queue.take(); // 队列空时自动阻塞
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
特点
* `put()` 和 `take()` 方法自动阻塞,简化线程同步。
* 支持有界队列和无界队列(如 `LinkedBlockingQueue`)。
4. 共享变量 + `volatile`
通过 `volatile` 关键字保证变量的内存可见性,实现简单状态标记。
适用场景
简单的线程状态控制(如终止线程)。
示例
java
public class VolatileDemo {
private volatile boolean running = true;
public void stop() {
running = false;
}
public void runTask() {
new Thread(() -> {
while (running) {
// 执行任务
}
System.out.println("线程终止");
}).start();
}
}
特点
* `volatile` 保证变量的修改对其他线程立即可见。
* **不能保证复合操作的原子性**(如 `i++`)。
5. 管道通信(`PipedInputStream` / `PipedOutputStream`)
通过管道流实现线程间数据传输。
示例
java
import java.io.*;
public class PipedCommunication {
public static void main(String[] args) throws IOException {
final PipedOutputStream output = new PipedOutputStream();
final PipedInputStream input = new PipedInputStream(output);
// 生产者线程
new Thread(() -> {
try {
output.write("Hello from producer!".getBytes());
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
int data;
while ((data = input.read()) != -1) {
System.out.print((char) data);
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
特点
* 适合线程间直接传输字节流或字符流。
* 实际开发中使用较少,通常用更高级的队列代替。
6. 同步工具类(`CountDownLatch`, `CyclicBarrier`, `Semaphore`)
Java 并发包中的工具类支持更复杂的线程协作。
示例(CountDownLatch)
java
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("子线程执行任务");
latch.countDown(); // 计数器减1
}).start();
}
latch.await(); // 等待所有子线程完成
System.out.println("所有子线程执行完毕");
}
}
特点
* `CountDownLatch`:一次性使用,等待多个线程完成。
* `CyclicBarrier`:可重复使用,多个线程互相等待到屏障点。
* `Semaphore`:控制同时访问资源的线程数。
总结
* **简单协作**:优先使用 `BlockingQueue` 或 `volatile`。
* **复杂条件**:用 `Lock` + `Condition` 或 `synchronized` + `wait()/notify()`。
* **数据传输**:选择 `BlockingQueue` 或管道流。
* **线程同步工具**:合理使用 `CountDownLatch`、`CyclicBarrier` 等。
**注意**:避免死锁(确保锁的获取顺序一致)、竞态条件(正确同步共享资源)和过度同步(影响性能)。