在多线程编程中,线程之间的通讯是至关重要的一环。它允许多个线程之间进行有效的数据交换和状态协调,以确保多线程程序的正确性和高效性。本文将介绍几种常见的Java线程通讯方式。
一、wait()、notify() 和 notifyAll()
这是最基础的线程间通信方式,它们需要与synchronized关键字一起使用
public class WaitNotifyExample {
private final Object lock = new Object();
private boolean isProduced = false;
public void produce() throws InterruptedException {
synchronized (lock) {
if (isProduced) {
lock.wait(); // 等待消费完成
}
// 生产逻辑
System.out.println("Produced...");
isProduced = true;
lock.notifyAll(); // 唤醒所有等待的线程
}
}
public void consume() throws InterruptedException {
synchronized (lock) {
if (!isProduced) {
lock.wait(); // 等待生产完成
}
// 消费逻辑
System.out.println("Consumed...");
isProduced = false;
lock.notifyAll(); // 唤醒所有等待的线程
}
}
}
优点:简单直接。
缺点:容易出错,例如忘记唤醒其他线程或错误地调用wait()/notify()
二、volatile 关键字
volatile可以确保变量对所有线程可见,但不能保证原子性
public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // 写入操作
}
public void reader() {
while (!flag) {
// 等待直到flag变为true
}
// 执行后续逻辑
}
}
优点:轻量级,性能开销小。
缺点:只能用于简单的状态标志,不能处理复杂的数据结构或复合操作
三、使用Lock接口及其Condition类
Lock接口提供了更强大的锁机制,而Condition对象则用来替代传统的Object监控器方法
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean isProduced = false;
public void produce() throws InterruptedException {
lock.lock();
try {
if (isProduced) {
condition.await(); // 等待消费完成
}
// 生产逻辑
System.out.println("Produced...");
isProduced = true;
condition.signalAll(); // 唤醒所有等待的线程
} finally {
lock.unlock();
}
}
public void consume() throws InterruptedException {
lock.lock();
try {
if (!isProduced) {
condition.await(); // 等待生产完成
}
// 消费逻辑
System.out.println("Consumed...");
isProduced = false;
condition.signalAll(); // 唤醒所有等待的线程
} finally {
lock.unlock();
}
}
}
优点:灵活性更高,支持多个条件变量。
缺点:相对复杂,可能会引入更多的并发问题
四、BlockingQueue
BlockingQueue提供了一种线程安全的方式来进行生产者-消费者模式的实现
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class BlockingQueueExample {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
public void producer() throws InterruptedException {
String data = null;
boolean retValue;
while (true) {
data = "data";
retValue = queue.offer(data, 2, TimeUnit.SECONDS);
if (retValue) {
System.out.println("Producer: " + data);
Thread.sleep(1000);
} else {
System.out.println("Failed to produce " + data);
}
}
}
public void consumer() throws InterruptedException {
String result = null;
while (true) {
result = queue.poll(2, TimeUnit.SECONDS); // 超时获取
if (null != result) {
System.out.println("Consumer: " + result);
Thread.sleep(2500);
} else {
System.out.println("Poll failed");
}
}
}
}
优点:简化了生产者-消费者的实现,内置了阻塞特性。
缺点:对于某些特定场景可能不够灵活
无论采用哪种方式,都应特别注意死锁、活锁等并发问题,并尽可能使用高级抽象如BlockingQueue来减少并发编程的复杂度