2016-11-09 66 views
1

場景:訪問共享資源,鎖定解鎖或等待通知

多線程從不同來源讀取。
一個單一的接入點到一個共享隊列(參見類RiderSynchronized嘗試寫)
每一行一個Reader讀取,它試圖插入到共享隊列通過方法RiderSynchronized提供。

當共享隊列已滿,我必須在一份聲明中插入到Oracle運行批處理。同時,所有對共享隊列的訪問都必須被拒絕。

代碼:

public class RiderSynchronized { 

    private ArrayDeque<JSONRecord> queue = new ArrayDeque<>(); 
    private OracleDAO oracleDao; 
    private long capacity; 

    public RiderSynchronized(OracleDAO oracleDao, long capacity) { 
     this.oracleDao = oracleDao; 
     this.capacity = capacity; 
    } 

    public synchronized boolean addRecord(JSONRecord record) { 
     boolean success = false; 
     try { 
      while (queue.size() >= capacity) { 
       wait(); 
      } 

      queue.add(record); 
      if (queue.size() < capacity) { 
       success = true; 
       notify(); //notify single Thread 
      } else { 
       JSONRecord currentRecord = null; 
       while ((currentRecord = queue.poll()) != null) { 
        oracleDao.insertRowParsedIntoBatch(currentRecord); 
       } 
       oracleDao.runBatch(); 
       success = true; 
       notifyAll(); //it could be all Reading Threads are waiting. Notify all 

      } 

     } catch (Exception e) { 
      success = false; 
     } 
     return success; 
    } 
} 

我不得不承認我是有點擔心的事情。

1)閱讀器線程可以直接使用的addRecord含糊不清?他們會等待自己嗎?或者我必須執行一些其他的方法來檢查之前運行addRecord方法嗎?

2)當queue.size爲<時,我決定只通知一個線程,因爲恕我直言,此時沒有線程應該處於狀態等待狀態。我錯了嗎?我應該通知所有?

2b)「else」語句的確切問題。通知全部是否是一個好習慣?在這一點上,它可能是所有的等待?

3)最後。我有點擔心使用Lock e Condition Classes重寫所有內容。這是一個更好的決定嗎?或者可以,我如何運行這種情況?

回答

1

1)讀者線程可以模糊地使用addRecord?他們要去 等待自己嗎?或者我必須執行一些其他方法 在哪裏檢查之前運行addRecord方法?

與您當前密碼的問題是,如果由於某種原因notifyAll不受理論上應該能夠進入else塊的唯一線程調用然後你的線程會永遠等待

在你的代碼中的潛在風險是:

  • oracleDao.insertRowParsedIntoBatch(currentRecord)
  • oracleDao.runBatch()

根據您當前的代碼,如果這些方法之一拋出異常notifyAll將永遠不會被調用,所以你的線程將永遠等待,你至少應該考慮在finally塊中調用notifyAll以使s它會被稱爲是否發生。


2)當queue.size <能力,我決定通知只是一個線程, 因爲恕我直言,在這一點上,沒有線程應該是處於等待狀態。 我錯了嗎?我應該通知所有?

你的線程只能在案件queue.size() >= capacity等那麼我notify甚至沒有需要,因爲這條件(queue.size() < capacity)沒有任何線程的預期。


2B)爲 「其他」 語句準確的問題。 notifyAll是否是一個好習慣?在這一點上,它可能是所有的等待?

69項來自Effective Java

一個相關的問題是,你是否應該使用notifynotifyAll喚醒 等待的線程。 (回想一下notify會喚醒一個等待線程, 假設這樣一個線程存在,並且notifyAll會喚醒所有等待 線程。)通常說你應該總是使用notifyAll。這個 是合理的,保守的建議。它總是會產生正確的 結果,因爲它保證你會喚醒需要喚醒 的線程。您也可以喚醒其他一些線程,但這不會影響程序的正確性。這些線程將檢查他們正在等待的 條件,發現它爲假,將繼續等待 。作爲一種優化,如果所有可能處於等待集中的線程均爲 ,並且一次只有一個線程可從 中受益,則可以選擇調用notify 而不是notifyAll。即使這些條件 顯示爲真,也可能有原因使用notifyAll代替通知。 正如在循環中放置等待調用可防止 對公共可訪問對象發生意外或惡意通知, 使用notifyAll代替notify可防止意外或非相關線程的惡意等待。這樣的等待可以以其他方式 「吞下」爲重要通知,使其預期收件人 無限期地等待。


3)最後。我有點擔心重寫所有使用Lock e 條件分類。這是一個更好的決定嗎?或者可以嗎我是如何運行這種情況的 ?

LockCondition如果你需要的是不可用例如像tryLock()或醒來只等待一個給定條件的線程的能力,內在鎖功能很有趣。在你的情況下,它似乎沒有必要,所以你可以保持它的樣子。

1

我會盡力一一回答你的問題。

1)如果我理解你正確的答案是是的線程將等待,你不需要做任何事情。

2)你不需要在queue.size < capacity的情況下,通知任何人有在這一點上沒有等待的線程。

3)它是確定通知所有。如果比容量更多的線程正在等待,其他的線程快速到達wait狀態。

4)是基於意見的問題。在你的場景中,你不會從重寫中獲得任何好處。