在Java多线程编程中,wait()
, notify()
, 和 notifyAll()
是实现线程间协作的关键方法,它们用于线程间的通信和同步。本文将介绍这些方法的工作原理,常见问题、易错点及其避免策略,并给出代码示例。
1. 基本概念
这些方法都是Object
类的成员,只能在synchronized
代码块或方法中使用。它们用于控制线程的执行顺序,协调共享资源的访问。
- wait() :让当前线程等待,释放它持有的锁,直到其他线程调用
notify()
或notifyAll()
唤醒它。 - notify() :唤醒在当前对象上等待的一个线程,如果有多条线程等待,随机选择一个。
- notifyAll() :唤醒在当前对象上等待的所有线程。
2. 常见问题与避免策略
- 死锁:不当使用
wait()
,notify()
, 和notifyAll()
可能导致死锁。确保正确设置和释放锁,避免循环等待。 - 未捕获异常:
wait()
会抛出InterruptedException
,需要捕获并处理。忽略异常可能导致程序终止。 - 不在同步块中使用:不在
synchronized
上下文中调用这些方法,可能导致线程安全问题。 - 通知未等待的线程:
notify()
或notifyAll()
可能唤醒正在运行的线程,而非等待的线程。确保等待的线程已进入wait()
状态。
3. 示例:生产者-消费者模型
public class Buffer {
private int data;
private final Object lock = new Object();
public void produce(int value) {
synchronized (lock) {
while (data != 0) {
try {
lock.wait(); // 生产者等待,直到消费者消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data = value;
lock.notify(); // 唤醒等待的消费者
}
}
public int consume() {
synchronized (lock) {
while (data == 0) {
try {
lock.wait(); // 消费者等待,直到生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int value = data;
data = 0;
lock.notify(); // 唤醒等待的生产者
return value;
}
}
}
public class Main {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Thread producer = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
buffer.produce(i);
}
});
Thread consumer = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
System.out.println("Consumed: " + buffer.consume());
}
});
producer.start();
consumer.start();
}
}
这个例子展示了生产者-消费者模型的实现,使用wait()
和notify()
实现线程间的协作。
4. 总结
理解并正确使用wait()
, notify()
, 和 notifyAll()
是实现线程协作的关键。在实际编程中,确保它们在synchronized
上下文中使用,正确处理异常,避免死锁,可以编写出更加健壮的多线程程序。