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,而不是鎖。 
相關文章
相關標籤/搜索