简单的说,notify()只唤醒一个正在等待的线程,当该线程执行完以后施放该对象的锁,而没有再次执行notify()方法,则其它正在等待的线程
则一直处于等待状态,不会被唤醒而进入该对象的锁的竞争池,就会发生死锁。
 
JVM多个线程间的通信是通过 线程的锁、条件语句、以及wait()、notify()/notifyAll组成。
下面来实现一个启用多个线程来循环的输出两个不同的语句:
package com.tyxh.block;
 
class OutTurn {
    private boolean isSub = true;
    private int count = 0;
 
    public synchronized void sub() {
          try {
              while (!isSub ) {
                  this.wait();
             }
             System. out.println("sub  ---- " + count);
              isSub = false ;
              this.notify();
         } catch (Exception e) {
             e.printStackTrace();
         }
          count++;
 
    }
 
    public synchronized void main() {
          try {
              while (isSub ) {
                  this.wait();
             }
             System. out.println("main (((((((((((( " + count);
              isSub = true ;
              this.notify();
         } catch (Exception e) {
             e.printStackTrace();
         }
          count++;
    }
}
package com.tyxh.block;
 
public class LockDemo {
    public static void main(String[] args) {
          // System.out.println("lock");
 
          final OutTurn ot = new OutTurn();
 
          for (int j = 0; j < 100; j++) {
 
              new Thread(new Runnable() {
 
                  public void run() {
                       // try {
                       // Thread.sleep(10);
                       // } catch (InterruptedException e) {
                       // e.printStackTrace();
                       // }
                       for (int i = 0; i < 5; i++) {
                          ot.sub();
                      }
                 }
             }).start();
 
              new Thread(new Runnable() {
 
                  public void run() {
                       // try {
                       // Thread.sleep(10);
                       // } catch (InterruptedException e) {
                       // e.printStackTrace();
                       // }
                       for (int i = 0; i < 5; i++) {
                          ot.main();
                      }
                 }
             }).start();
         }
 
    }
}
 
解释一下原因:
     OutTurn类中的sub和main方法都是同步方法,所以多个调用sub和main方法的线程都会处于阻塞状态,等待一个正在运行的线程来唤醒它们。下面分别分析一下使用notify和notifyAll方法唤醒线程的不同之处:
     上面的代码使用了notify方法进行唤醒,而notify方法只能唤醒一个线程,其它等待的线程仍然处于wait状态,假设调用sub方法的线程执行完后(即System. out .println("sub  ---- " + count )执行完之后),所有的线程都处于等待状态,此时在sub方法中的线程执行了isSub=false语句后又执行了notify方法,这时如果唤醒的是一个sub方法的调度线程,那么while循环等于true,则此唤醒的线程也会处于等待状态,此时所有的线程都处于等待状态,那么也就没有了运行的线程来唤醒它们,这就发生了死锁。
     如果使用notifyAll方法来唤醒所有正在等待该锁的线程,那么所有的线程都会处于运行前的准备状态(就是sub方法执行完后,唤醒了所有等待该锁的状态,注:不是wait状态),那么此时,即使再次唤醒一个sub方法调度线程,while循环等于true,唤醒的线程再次处于等待状态,那么还会有其它的线程可以获得锁,进入运行状态。
 
     总结:notify方法很容易引起死锁,除非你根据自己的程序设计,确定不会发生死锁,notifyAll方法则是线程的安全唤醒方法。
 
附:
notify和notifyAll的区别:

notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。

void notify(): 唤醒一个正在等待该对象的线程。
void notifyAll(): 唤醒所有正在等待该对象的线程。
两者的最大区别在于:

notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。

     notify他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。 

java中为什么notify()可能会导致死锁,而notifyAll()则不会的更多相关文章

  1. Java中为什么notify()可能导致死锁,而notifyAll()则不会(针对生产者-消费者模式)

    1.先说两个概念:锁池 和 等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线 ...

  2. java中的notify和notifyAll有什么区别?

    先说两个概念:锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入 ...

  3. Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例

    如何使用Wait 尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单.如果你在面试中让应聘者来手写代码,用wait和notify解决生产者消费者问题 ...

  4. java中线程安全,线程死锁,线程通信快速入门

    一:多线程安全问题 ###1 引入 /* * 多线程并发访问同一个数据资源 * 3个线程,对一个票资源,出售 */ public class ThreadDemo { public static vo ...

  5. java中wait/notify机制

    通常,多线程之间需要协调工作.例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程 downloadThread将该图片下载完毕.如果图片还没有下载完,d ...

  6. 面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?

    概念 内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用.即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费. 可达 ...

  7. C# 实现java中 wiat/notify机制

    最近在学习java,看到wiat/notify机制实现线程通信,由于平时工作用的C#,赶紧用C#方式实现一个demo. Java 代码: import java.util.ArrayList; imp ...

  8. 详解java中CAS机制所导致的问题以及解决——内存顺序冲突

    [CAS机制] 指的是CompareAndSwap或CompareAndSet,是一个原子操作,实现此机制的原子类记录着当前值的在内存中存储的偏移地址,将内存中的真实值V与旧的预期值A做比较,如果不一 ...

  9. java中异步多线程超时导致的服务异常

    在项目中为了提高大并发量时的性能稳定性,经常会使用到线程池来做多线程异步操作,多线程有2种,一种是实现runnable接口,这种没有返回值,一种是实现Callable接口,这种有返回值. 当其中一个线 ...

随机推荐

  1. Shell 编程 case语句

    本篇主要写一些shell脚本case语句的使用. 字符判断 #!/bin/bash read -p "请输入一个字符:" char case $char in [a-z]|[A-Z ...

  2. memcached的安装和学习

    linux下安装memcached 第一首先要安装 libvevent ,用 rpm -qa | preg libevent  ,用 which libevent  查看安装路径 以下图解:     ...

  3. phpstorm webstorm编辑器正则替换 类名,方法名替换

    首先勾选Match Case 和 Regex 正则规则:无须添加//左右分解符,直接写正则表达式,注意应该转义的部分,需要原封不动替换的部分加上括号 替换规则:正常书写正则,要继承下来的字符使用$1. ...

  4. CentOS6.7搭建部署DNS服务 (详解主配置文件)

    -DNS服务器的基本原理 域名简介:使用tcp/udp协议,默认使用53端口号 默认情况下,客户端发起的查询请求都是用UDP/53查询的. 默认情况下,从从服务器到主服务器传输数据用的是TCP/53. ...

  5. c小例子 10个数找出第一名

    这个小例子,是从十个数中找到第一名,如果第一名的分数相同,则二者都可以晋级,进入下一轮比赛,我们就对这个数进行排序,这样要求输出晋级人员的标号0-9号中其中一个或几个. 如何用c语言来实现呢? 1) ...

  6. springboot 打包插件spring-boot-maven-plugin打包机制及内部结构分析

    今日思语:I miss you? 何解? 我错过你了?我想你了? 当下许多公司都会选择使用springboot作为服务应用开发框架,springboot框架提供了一套自己的打包机制,是通过spring ...

  7. Java逆变(Covariant)和协变(Contravariant)

    1. 定义 逆变和协变描述的经过类型变换后的类型之间的关系.假如A和B表示类型,f表示类型变换,A ≤B表示A是B的子类型,那么 如果A ≤B,f(A) ≤f(B),那么f是协变 如果A ≤B,f(B ...

  8. FitNesseRoot/ErrorLogs目录下可查看fitnesse输出日志

    调试fitnesse用例时,通过测试页面的输出信息不是很好定位问题出在哪里 这时可以在写代码过程中,增加一些输出信息,比如说java的话,可以用log4j.注意要把日志输出弄成utf-8编码,不然会中 ...

  9. [React] Use CSS Transitions to Avoid a Flash of Loading State

    Based on research at Facebook, we know that if a user sees a flash of loading state, they perceive t ...

  10. 临时加一条关于bootstrap的菜单栏方面的

    **有些生疏,记住了**aria-expanded表示展开状态.默认为undefined, 表示当前展开状态未知.其它可选值:true表示元素是展开的:false表示元素不是展开的. aria-hid ...