Java中notify()和notifyAll()的使用区别

 更新时间:2021年06月24日 11:50:23   作者:ConstXiong  
本文主要介绍了Java中notify()和notifyAll()的使用区别,文中通过示例代码介绍的非常详细,感兴趣的小伙伴们可以参考一下

notify() 和 notifyAll() 有什么区别?

先解释两个概念。

  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池,等待池中的线程不会去竞争该对象的锁。
  • 锁池:只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待

然后再来说notify和notifyAll的区别

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

有了这些理论基础,后面的notify可能会导致死锁,而notifyAll则不会的例子也就好解释了

测试代码

public class TestNotifyNotifyAll {
 
 private static Object obj = new Object();
 
 public static void main(String[] args) {
  
  //测试 RunnableImplA wait()        
  Thread t1 = new Thread(new RunnableImplA(obj));
  Thread t2 = new Thread(new RunnableImplA(obj));
  t1.start();
  t2.start();
  
  //RunnableImplB notify()
  Thread t3 = new Thread(new RunnableImplB(obj));
  t3.start();
  
  
//  //RunnableImplC notifyAll()
//  Thread t4 = new Thread(new RunnableImplC(obj));
//  t4.start();
 }
 
}
 
 
class RunnableImplA implements Runnable {
 
 private Object obj;
 
 public RunnableImplA(Object obj) {
  this.obj = obj;
 }
 
 public void run() {
  System.out.println("run on RunnableImplA");
  synchronized (obj) {
   System.out.println("obj to wait on RunnableImplA");
   try {
    obj.wait();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   System.out.println("obj continue to run on RunnableImplA");
  }
 }
}
 
class RunnableImplB implements Runnable {
 
 private Object obj;
 
 public RunnableImplB(Object obj) {
  this.obj = obj;
 }
 
 public void run() {
  System.out.println("run on RunnableImplB");
  System.out.println("睡眠3秒...");
  try {
   Thread.sleep(3000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  synchronized (obj) {
   System.out.println("notify obj on RunnableImplB");
   obj.notify();
  }
 }
}
 
class RunnableImplC implements Runnable {
 
 private Object obj;
 
 public RunnableImplC(Object obj) {
  this.obj = obj;
 }
 
 public void run() {
  System.out.println("run on RunnableImplC");
  System.out.println("睡眠3秒...");
  try {
   Thread.sleep(3000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  synchronized (obj) {
   System.out.println("notifyAll obj on RunnableImplC");
   obj.notifyAll();
  }
 }
}

结果:仅调用一次 obj.notify(),线程 t1 或 t2 中的一个始终在等待被唤醒,程序不终止

run on RunnableImplA
obj to wait on RunnableImplA
run on RunnableImplA
obj to wait on RunnableImplA
run on RunnableImplB
睡眠3秒...
notify obj on RunnableImplB
obj continue to run on RunnableImplA

把 t3 注掉,启动 t4 线程。调用 obj.notifyAll() 方法

public class TestNotifyNotifyAll { 
 private static Object obj = new Object();
  public static void main(String[] args) {
  
  //测试 RunnableImplA wait()        
  Thread t1 = new Thread(new RunnableImplA(obj));
  Thread t2 = new Thread(new RunnableImplA(obj));
  t1.start();
  t2.start();
  
//  //RunnableImplB notify()
//  Thread t3 = new Thread(new RunnableImplB(obj));
//  t3.start();
  
  
  //RunnableImplC notifyAll()
  Thread t4 = new Thread(new RunnableImplC(obj));
  t4.start();
 } 
}

结果:t1、t2线程均可以执行完毕

run on RunnableImplA
obj to wait on RunnableImplA
run on RunnableImplA
obj to wait on RunnableImplA
run on RunnableImplC
睡眠3秒...
notifyAll obj on RunnableImplC
obj continue to run on RunnableImplA
obj continue to run on RunnableImplA

到此这篇关于Java中notify()和notifyAll()的使用区别的文章就介绍到这了,更多相关Java notify()和notifyAll()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java实现图片裁切的工具类实例

    java实现图片裁切的工具类实例

    这篇文章主要介绍了java实现图片裁切的工具类实例,涉及Java针对图片的读取、修改等相关操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-11-11
  • Java深入探索单例模式的应用

    Java深入探索单例模式的应用

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
    2022-06-06
  • Java代码里如何拼接SQL语句到mybatis的xml

    Java代码里如何拼接SQL语句到mybatis的xml

    这篇文章主要介绍了Java代码里拼接SQL语句到mybatis的xml操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Jenkins如何实现自动打包部署linux

    Jenkins如何实现自动打包部署linux

    这篇文章主要介绍了Jenkins如何实现自动打包部署linux,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • java编程实现邮件定时发送的方法

    java编程实现邮件定时发送的方法

    这篇文章主要介绍了java编程实现邮件定时发送的方法,涉及Java基于定时器实现计划任务的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-11-11
  • httpclient提交json参数的示例详解

    httpclient提交json参数的示例详解

    httpclient使用post提交json参数,和使用表单提交区分,本文结合示例代码讲解的非常详细,补充介绍了HttpClient请求传json参数的案例代码,感兴趣的朋友一起看看吧
    2024-02-02
  • Java中的动态和静态编译实例详解

    Java中的动态和静态编译实例详解

    这篇文章主要介绍了Java中的动态和静态编译实例详解的相关资料,需要的朋友可以参考下
    2017-04-04
  • java中concat()方法的使用说明

    java中concat()方法的使用说明

    这篇文章主要介绍了java中concat()方法的使用说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • java判断域名无法访问自行访问下一条

    java判断域名无法访问自行访问下一条

    这篇文章主要为大家介绍了java实现判断域名无法访问的时候自行访问下一条域名示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Java使用正则表达式匹配获取链接地址的方法示例

    Java使用正则表达式匹配获取链接地址的方法示例

    这篇文章主要介绍了Java使用正则表达式匹配获取链接地址的方法,简单分析了java正则匹配常用方法及获取网址链接的相关操作技巧,需要的朋友可以参考下
    2017-08-08

最新评论