Thread Signaling

    本文大概意思都是都下邊連接文章轉換過來的,沒有進行一字一句的翻譯,只是把大概意思整裏出來。
html

       http://tutorials.jenkov.com/java-concurrency/thread-signaling.htmljava

     線程信號的目的是爲了線程之間相互通訊。線程信號可使線程等待另其餘線程信號,例如thread B 或許等待從thread A 發出的數據準備處理信號。多線程


1  Signaling via Shared Objects

    線程之間能夠經過共享對象來相互發送信號。Thread A 能夠經過在synchronized同步塊裏設置變量 hasDataToProcess爲true ,線程B一樣在synchronized同步塊裏讀取hasDataToProcess的值來肯定是否有數據可讀。this

     下面是一個簡單的信號對象spa

 public class MySignal{

  protected boolean hasDataToProcess = false;
  
  public synchronized boolean hasDataToProcess(){
    return this.hasDataToProcess;
  }
  
  public synchronized void setHasDataToProcess(boolean hasData){
    this.hasDataToProcess = hasData;  
  }
}

    thread A 與 thread B 必要經過同一個MySignal 實例來進行通訊。若是 A B 引用了不一樣的MySignal實例,它們之間將不會接收到相互的信號。.net

2 Busy Wait

    因爲thread B 一直在等待是否有數據能夠處理,若是採用上面的MySignal實例,那麼thread B 必須一直循環調用hasDataToProcess來判斷是否有數據處理。這樣就會產生忙等,消耗大量的CPU。線程

3 wait(), notify() and notifyAll()

     忙等除了在平均時間較短的狀況下比較有效,其餘狀況不能有效的利用CPU。若是線程在收到信號以前能夠sleep,翻譯

  或者變成inactive 狀態。java 內嵌了使等待線程變成inactive狀態的機制。使用java.lang.Object 上面的wait(),notify(),code

  notifyAll()能夠實現。當一個線程在任意object 上調用wait(),它會變成inactive狀態,直到有其餘線程在該object上調用notify(),該線程纔會繼續執行。爲了調用wait 或者notify ,調用線程必須獲取在object上的鎖。換句話說調用線程必須在同步塊裏調用wait ,notify,notifyAll。htm

    下面是改造版的信號類MyWaitNotify    

  public class MyWaitNotify{
	
      Object myMonitorObject = new Object();

      public void doWait(){
         synchronized(myMonitorObject){
          try{
            myMonitorObject.wait();
          } catch(InterruptedException e){...}
        }
      }

      public void doNotify(){
        synchronized(myMonitorObject){
          myMonitorObject.notify();
        }
      }
}

    

    等待線程能夠調用doWait,發出通知的線程能夠調用doNotify。當一個線程在某個object上調用notify,全部在該object等待的線程,只有一個線程將會被喚醒繼續執行代碼。若是調用notifyAll能夠喚醒該對象上全部等待的線程。全部的等待線程 ,通知線程都必須在synchronized 中調用wait,notify ,notifyAll。這是強制的。一個線程若是沒有獲取一個object上的鎖,將不能調用這幾個方法。不然會拋出IllegalMonitorStateException異常。一旦一個線程調用wait ,它將釋放它所持有的monitor object上的鎖。這樣就能夠容許其餘線程調用同步塊中的wait 或者notify 。當一個線程未離開notify同步塊以前,喚醒的線程不能退出wait調用塊,由於沒有得到monitor object 上的鎖。

4 Missed Signals

    notify,notifyAll 不保存對它們的方法調用當沒有線程等待在該monitor object。notify 信號就丟死了。所以,若是一個線程在調用wait以前調用notify,信號將會被等待線程丟失。這樣在一些案例中將致使等待線程一直在等待,不會醒來,由於通知信號被丟失了。爲了不信號的丟失,信號能夠被存儲在signal類中。

    下面是會存儲信號的MyWaitNotify類

public class MyWaitNotify2{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      if(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }
  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}


5 Spurious Wakeups

    虛假喚醒指的是即便線程沒有調用監視器對象上的notify或者notifyAll,等待線程就醒了。喚醒可能沒有任何緣由的發生。若是一個虛假喚醒發生在MyWaitNofiy2中的doWait()中,等待線程在沒有接收到一個恰當的信號就開始執行。這會致使嚴重的後果。下邊把MyWaitNotify2中的if 判斷改成while循環,只有wasSignalled狀態改變,才認爲是notify真正的被髮出。

public class MyWaitNotify3{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

改爲while循環後,即便發生虛假喚醒,若是wasSignalled的值沒有被改變,那麼線程將再次調用wait(),使線程變爲inactive狀態。

6 Multiple Threads Waiting for the Same Signals

        while 循環一樣在多線程等待的狀況中還是一個很好的解決方案。當監視器上的notifyAll被調用時,只有其中一個線程會被容許繼續執行,其餘的將會被再次等待,由於其餘線程得到鎖後,wasSignalled上的信號已經被第一個喚醒的線程擦除掉了。

7 Don't call wait() on constant String's or global objects

        當線程把一個把常量字符串看成監視器對象時,會出現異常。緣由是JVM/Compiler內部會把常量字符串轉換爲同一對象對待。這意味着即便你有兩個不一樣的MyWaitNotify實例,它們裏面的監視器對象將會是同一個字符對象。這意味着若是一個線程在第一信號實例調用wait方法,可能將會被另外一信號實例上notify的調用喚醒。所以不要把全局對象,string 常量看成監視器對象用。

本文連接: http://my.oschina.net/robinyao/blog/611885

相關文章
相關標籤/搜索