notify() and notifyAll():

本文深入解析Java线程唤醒机制中的notify()和notifyAll()方法的区别,包括它们如何唤醒等待线程以及各自在多线程同步控制中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

notify() and notifyAll():


notify() wakes up one thread and releases the lock. It is to send wake-up command to another one and only one thread that is in wait() status and is waiting for a notify command. After waking up, it will compete for the lock and as it is the only thread that was awake, it will get the lock and run. 


notifyAll() wakes up all waiting threads and releases the lock. It is to wake up other threads that are in wait() status and are waiting for a wake-up command (rather than the lock). After waking up, they will compete for the lock and the only one thread that gets the lock will run. Other threads are in ready state, not in wait() status (not waiting for wake-up command, but wait for lock). 
### Java 中 `notify()` 方法导致死锁的原因及解决办法 `notify()` 是 Java 中用于线程间通信的一个重要方法,但它可能导致死锁的情况。以下是具体原因及其解决方案。 #### 1. **死锁产生的主要原因** 当多个线程共享同一个对象锁时,如果其中一个线程调用了 `notify()` 而其他线程并未处于等待状态,则可能会引发死锁情况[^4]。这是因为: - `notify()` 只随机唤醒一个正在等待的对象上的单个线程。 - 如果被唤醒的线程并不具备继续执行所需的条件(例如资源未准备好),它可能再次进入等待状态,而此时其他线程也可能因为同样的原因陷入无限等待。 这种情况下,系统中的所有相关线程都可能停止运行,形成死锁。 #### 2. **典型示例分析** 以下是一个典型的生产者消费者模型中可能出现的死锁案例: ```java class Queue { private List<Integer> list = new ArrayList<>(); public synchronized void put(int value) throws InterruptedException { while (list.size() >= 5) { wait(); } list.add(value); System.out.println("Produced " + value); notify(); // 唤醒任意一个等待的线程 } public synchronized int get() throws InterruptedException { while (list.isEmpty()) { wait(); } int value = list.remove(0); System.out.println("Consumed " + value); notify(); // 唤醒任意一个等待的线程 return value; } } ``` 在这个例子中,如果只有一个消费者和一个生产者,并且两者交替工作正常,但如果引入更多线程(如两个消费者或两个生产者),则可能发生如下问题: - 当某个线程被 `notify()` 唤醒后发现条件仍不满足(队列为空或满),它会重新进入 `wait()` 状态。 - 这种情况下,另一个线程可能永远得不到通知,从而造成死锁。 --- #### 3. **解决方案** ##### (1)使用 `notifyAll()` 替代 `notify()` 通过调用 `notifyAll()` 来唤醒所有等待在同一对象上的线程,可以有效减少死锁的风险。这种方式确保了所有潜在的相关线程都能有机会检查自己的条件是否满足。 修改后的代码如下所示: ```java public synchronized void put(int value) throws InterruptedException { while (list.size() >= 5) { wait(); } list.add(value); System.out.println("Produced " + value); notifyAll(); // 唤醒所有等待的线程 } public synchronized int get() throws InterruptedException { while (list.isEmpty()) { wait(); } int value = list.remove(0); System.out.println("Consumed " + value); notifyAll(); // 唤醒所有等待的线程 return value; } ``` ##### (2)增加超时机制 为了避免线程长时间等待而导致死锁,可以在 `wait(long timeout)` 上设置超时时间。这样即使条件未能及时满足,线程也可以定期醒来重试[^5]。 示例代码调整为: ```java public synchronized void put(int value) throws InterruptedException { long startTime = System.currentTimeMillis(); final long timeoutMillis = 100; // 设置最大等待时间为100毫秒 while (list.size() >= 5 && ((System.currentTimeMillis() - startTime) < timeoutMillis)) { wait(timeoutMillis - (System.currentTimeMillis() - startTime)); } if(list.size() < 5){ list.add(value); System.out.println("Produced " + value); notifyAll(); }else{ throw new IllegalStateException("Queue is full and cannot produce more."); } } ``` ##### (3)改进资源获取逻辑 在线程被唤醒后,应始终验证当前条件是否确实满足再继续操作。如果不满足,则需重新进入等待状态。这可以通过循环检测的方式实现[^4]。 例如,在消费者的 `get` 方法中加入额外判断: ```java public synchronized int get() throws InterruptedException { while (list.isEmpty()) { // 循环直到条件满足 wait(); } int value = list.remove(0); System.out.println("Consumed " + value); notifyAll(); return value; } ``` --- ### 总结 `notify()` 导致死锁的主要原因是其仅唤醒单一线程的行为容易使某些线程错过必要的信号。为了防止这种情况的发生,推荐采用 `notifyAll()` 或结合超时机制以及更严谨的条件检测策略来优化程序设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值