文章目录
- Java面试必看:notify()与notifyAll区别?点睛之笔!
- 引言
- 什么是 notify() 和 notifyAll()
- 基本概念
- 为什么会有 notify() 和 notifyAll()?
- notify() 和 notifyAll() 的核心区别
- 1. 唤醒范围不同
- `notify()`:只唤醒一个线程
- `notifyAll()`:唤醒所有线程
- 2. 使用场景不同
- `notify()` 的适用场景
- `notifyAll()` 的适用场景
- 3. 性能考虑
- 4. 注意事项
- 总结
- 在实际开发中,合理选择 `notify()` 和 `notifyAll()` 对系统的性能和稳定性有重要影响。
- 📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!
Java面试必看:notify()与notifyAll区别?点睛之笔!
引言
大家好,我是闫工,今天我们要聊一个Java面试中经常被问到的考点——notify()和notifyAll()的区别。这两个方法在多线程编程中非常重要,但它们的区别却常常让人一头雾水。别担心,我会用轻松幽默的方式为你详细解析,保证让你一学就会!
什么是 notify() 和 notifyAll()
在Java中,线程之间的通信离不开通知机制,而notify()和notifyAll()就是实现这种机制的两个关键方法。它们都属于Object类,因此任何对象都可以调用这两个方法。
基本概念
- notify(): 这个方法会唤醒一个正在等待该对象监视器的线程。
- notifyAll(): 这个方法会唤醒所有正在等待该对象监视器的线程。
简单来说,notify()是“点名”叫醒某一个线程,而notifyAll()则是“广撒网”,把所有睡着的线程都叫醒。
为什么会有 notify() 和 notifyAll()?
在线程编程中,我们经常需要多个线程协作完成任务。比如生产者-消费者模型:一个或多个生产者线程负责生成数据,一个或多个消费者线程负责消费数据。如果没有通知机制,这些线程可能会陷入“忙等”状态,导致性能低下甚至死锁。
notify()和notifyAll()的作用就是让线程之间能够高效地通信。通过它们,我们可以控制哪些线程被唤醒,从而优化程序的执行效率。
notify() 和 notifyAll() 的核心区别
1. 唤醒范围不同
notify():只唤醒一个线程
notify()会随机选择一个正在等待该对象监视器的线程,并将其唤醒。剩下的线程仍然保持阻塞状态,直到再次被通知或中断。
示例代码:
publicclassNotifyExample{publicstaticvoidmain(String[]args){Objectlock=newObject();// 创建多个线程for(inti=0;i<5;i++){newThread(()->{synchronized(lock){System.out.println(Thread.currentThread().getName()+" 进入等待状态");try{lock.wait();}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" 被唤醒");}}).start();}// 主线程通知newThread(()->{synchronized(lock){try{Thread.sleep(2000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("主线程调用 notify()");lock.notify();// 只唤醒一个线程}}).start();}}运行结果:
Thread-0 进入等待状态 Thread-1 进入等待状态 Thread-2 进入等待状态 Thread-3 进入等待状态 Thread-4 进入等待状态 主线程调用 notify() Thread-0 被唤醒从结果可以看出,notify()只唤醒了一个线程(比如Thread-0),其他线程仍然处于阻塞状态。
notifyAll():唤醒所有线程
notifyAll()会将所有正在等待该对象监视器的线程都唤醒。这通常用于需要让所有线程继续执行的情况。
示例代码:
publicclassNotifyAllExample{publicstaticvoidmain(String[]args){Objectlock=newObject();// 创建多个线程for(inti=0;i<5;i++){newThread(()->{synchronized(lock){System.out.println(Thread.currentThread().getName()+" 进入等待状态");try{lock.wait();}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" 被唤醒");}}).start();}// 主线程通知newThread(()->{synchronized(lock){try{Thread.sleep(2000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("主线程调用 notifyAll()");lock.notifyAll();// 唤醒所有线程}}).start();}}运行结果:
Thread-0 进入等待状态 Thread-1 进入等待状态 Thread-2 进入等待状态 Thread-3 进入等待状态 Thread-4 进入等待状态 主线程调用 notifyAll() Thread-0 被唤醒 Thread-1 被唤醒 Thread-2 被唤醒 Thread-3 被唤醒 Thread-4 被唤醒从结果可以看出,notifyAll()唤醒了所有线程。
2. 使用场景不同
notify()的适用场景
notify()适用于只需要唤醒一个线程的场景。例如:
- 单生产者-多消费者模型:当一个生产者生成数据后,只需要通知一个消费者来处理。
- 需要控制资源竞争的场景。
示例代码:
publicclassProducerConsumer{privateObjectlock=newObject();privatebooleanhasProduct=false;publicvoidproduce(){synchronized(lock){if(hasProduct){try{lock.wait();// 等待消费者消费}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println("生产者生产了产品");hasProduct=true;lock.notify();// 唤醒一个消费者}}publicvoidconsume(){synchronized(lock){if(!hasProduct){try{lock.wait();// 等待生产者生产}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println("消费者消费了产品");hasProduct=false;lock.notify();// 唤醒一个生产者}}publicstaticvoidmain(String[]args){ProducerConsumerpc=newProducerConsumer();// 创建多个消费者线程for(inti=0;i<3;i++){newThread(()->pc.consume()).start();}// 创建生产者线程newThread(()->{while(true){pc.produce();try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}}}).start();}}notifyAll()的适用场景
notifyAll()适用于需要唤醒所有线程的场景。例如:
- 所有线程都需要重新竞争资源。
- 广播通知:比如某个条件满足后,所有线程都可以继续执行。
示例代码:
publicclassBroadcastExample{privateObjectlock=newObject();publicvoidcheckCondition(){synchronized(lock){System.out.println(Thread.currentThread().getName()+" 进入检查");if(!conditionMet()){// 假设条件未满足try{lock.wait();}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+" 条件满足,继续执行");}}publicvoidnotifyAllThreads(){synchronized(lock){System.out.println("设置条件为已满足");conditionMet();// 假设条件已满足lock.notifyAll();// 通知所有线程}}privatebooleanconditionMet(){returntrue;// 简化逻辑,假设条件已满足}publicstaticvoidmain(String[]args){BroadcastExampleexample=newBroadcastExample();// 创建多个检查线程for(inti=0;i<3;i++){newThread(()->example.checkCondition()).start();}// 主线程设置条件并通知所有线程newThread(()->example.notifyAllThreads()).start();}}3. 性能考虑
notify()的性能更高,因为它只唤醒一个线程。notifyAll()的性能较低,因为它需要唤醒多个线程。
因此,在大多数情况下,优先使用notify();只有在确实需要唤醒所有线程时才使用notifyAll()。
4. 注意事项
- 在调用
wait()和notify()方法时,必须持有同一对象的锁。 wait()必须在同步块中调用,否则会抛出IllegalMonitorStateException。- 使用
wait()和notify()需要特别注意避免死锁和资源竞争问题。
总结
| 特性 | notify() | notifyAll() |
|---|---|---|
| 唤醒线程数 | 1 | 所有 |
| 使用场景 | 单个唤醒 | 广播通知 |
| 性能 | 高 | 较低 |
| 资源竞争控制 | 更好 | 较差 |
在实际开发中,合理选择notify()和notifyAll()对系统的性能和稳定性有重要影响。
📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!
成体系的面试题,无论你是大佬还是小白,都需要一套JAVA体系的面试题,我已经上岸了!你也想上岸吗?
闫工精心准备了程序准备面试?想系统提升技术实力?闫工精心整理了1000+ 套涵盖前端、后端、算法、数据库、操作系统、网络、设计模式等方向的面试真题 + 详细解析,并附赠高频考点总结、简历模板、面经合集等实用资料!
✅ 覆盖大厂高频题型
✅ 按知识点分类,查漏补缺超方便
✅ 持续更新,助你拿下心仪 Offer!
📥免费领取👉 点击这里获取资料
已帮助数千位开发者成功上岸,下一个就是你!✨