zoukankan      html  css  js  c++  java
  • Java中为什么notify()可能导致死锁,而notifyAll()则不会(针对生产者-消费者模式)

    1、先说两个概念:锁池 和 等待池

    • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
    • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

    2、然后再来说 notify 和 notifyAll 的区别

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

    之前写过一个案例:Java多线程— —线程 虚假唤醒 问题剖析

    解释一下原因:

         Data类中的increment和decrement方法都是同步方法,所以多个调用过increment和decrement方法的线程都会进入等待池处于阻塞状态,等待一个正在运行的线程来唤醒它们。下面分别分析一下使用notify和notifyAll方法唤醒线程的不同之处:

    使用了notify方法进行唤醒,而notify方法只能唤醒一个线程,其它等待的线程仍然处于wait状态,假设调用 increment 方法的线程执行完后,所有的线程都处于等待状态,此时 又执行了notify方法,这时如果唤醒的是一个increment方法的调度线程,那么while循环等于true,则此唤醒的线程也会处于等待状态,此时所有的线程都处于等待状态,那么也就没有了运行的线程来唤醒它们,这就发生了死锁。

         如果使用notifyAll方法来唤醒所有正在等待该锁的线程,那么所有的线程都会处于运行前的准备状态(就是increment方法执行完后,唤醒了所有等待该锁的状态,注:不是wait状态),那么此时,即使再次唤醒一个increment方法调度线程,while循环等于true,唤醒的线程再次处于等待状态,那么还会有其它的线程可以获得锁,进入运行状态。


         总结:notify方法很容易引起死锁,除非你根据自己的程序设计,确定不会发生死锁,notifyAll方法则是线程的安全唤醒方法。

  • 相关阅读:
    错误 1 类,结构或接口成员声明中的标记"="无效
    转asp.net中的App_GlobalResources和App_LocalResources使用
    input type=file 上传文件样式美化(转载)
    Postman Post请求上传文件
    vuex 、store、state (转载)
    ES5、ES2015、ECMAScript6(转载)
    axios 用法简介(转载)
    js中const,var,let区别(转载)
    C#开发微信公众平台-就这么简单(附Demo)转载
    什么是 Native、Web App、Hybrid、React Native 和 Weex?(转载)
  • 原文地址:https://www.cnblogs.com/dk1024/p/15033499.html
Copyright © 2011-2022 走看看