java中為什么notify()可能會導致死鎖,而notifyAll()則不會


簡單的說,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,而不是鎖。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM