2014-10-19 77 views
1

我想讓我的生產者/消費者工作,我故意不想使用BlockingQueue在這裏瞭解更細的細節。我明白,當我調用object.wait()時,線程鬆開它的鎖並進入WAITING狀態,直到有人通知(notify/notifyAll)使其返回到BLOCKED狀態,並且如果線程獲取鎖,則該線程將轉到RUNNABLE。可以等待的線程,再次喚醒上下文切換

private class Consumer implements Runnable{ 
     private final MyQueue<Integer> queue; 
     public Consumer(MyQueue<Integer> queue){ 
      this.queue = queue; 
     } 
     @Override 
     public void run(){ 
      while(true){ 
       synchronized (queue) { 
        //Block till new available 
        while(queue.isEmpty()){ 
         try { 
          queue.wait(); 
         } catch (InterruptedException e) { 
          e.printStackTrace(); 
         } 
        } 
        //Consume 
        queue.dequeue(); 
        queue.notifyAll(); //Notify people waiting on queue full condition 
       } 
      } 
     } 
    } 

我的問題是有可能,(上面相對於代碼):

  1. 我的線程在等待和觀望後醒來(有些人通知) 和我得到了鎖
  2. 我做了一個檢查queue.isEmpty(),它不是空的,執行到下一行
  3. 就在下一行queue.dequeue()被執行之前,CPU上下文切換了我的線程。
  4. 下一頁當我得到我的CPU片和鎖,我運行queue.dequeue(),並說隊列爲空

我知道通常CPU調度程序提供了時間量子給每個線程,以避免上下文切換成本。

回答

1

這種情況不會發生,正是因爲對隊列的任何修改(或至少應該)都是從隊列上的同步塊完成的。因此,在while循環結束和dequeue()調用之間沒有其他線程可以修改隊列,因爲您的線程是持有鎖的那個線程。

當然,如果其他使用者也從隊列中刪除元素而未在隊列上同步,則會出現此問題。但這隻會是你的代碼中的一個錯誤。使用封裝是避免這些類型錯誤的最佳方法。如果您使用BlockingQueue,則BlockingQueue類將封裝對共享狀態的獨佔訪問權限,並且您不必確保對隊列的每個訪問都已正確同步,因爲BlockingQueue會爲您執行此操作。

+0

上下文切換的線程是否保存鎖定並退出執行? – Monish 2014-10-19 13:45:58

+0

線程保持鎖定狀態直到它離開同步塊。線程當前是否正在執行並不會改變任何內容。如果調度程序決定在while循環之後和調用dequeue之前立即切換到其他線程,則等待該鎖的所有其他線程將繼續被阻塞,直到持有鎖的線程重新計劃爲止,並繼續執行直到它離開同步塊。 – 2014-10-19 13:50:18

+0

謝謝!這使得它非常清晰:「線程保持鎖定狀態,直到它離開同步塊爲止,線程當前是否正在執行不會改變任何內容」。雖然有趣的是,當OS上下文切換時,它可能會減慢正在等待鎖定的線程 – Monish 2014-10-19 13:59:44