0

我有一個生產者消費者喜歡模式,其中一些線程正在創建數據並定期傳遞該數據塊以供其他線程使用。如何在跨線程傳遞數據時確保Java內存可見性

記住Java內存模型,我如何確保傳遞給消費者線程的數據具有完整的「可見性」?

我知道java.util.concurrent中的數據結構像ConcurrentLinkedQueue是專門爲此構建的,但我想盡可能低級別地執行此操作,而不利用這些數據結構,並且對於在涵蓋以確保內存可見性的一部分。

+0

*「關於在封面下發生了什麼以確保內存可見性部分完全透明。」* - 你不相信'ConcurrentLinkedQueue'嗎? –

+1

作爲一個附註,ConcurrentLinkedQueue對生產者/消費者來說並不好,因爲它沒有阻塞設施。通常,BlockingQueue實現對於生產者/消費者來說是理想的。 – jtahlborn

回答

1

如果你想要「低級」,然後看看volatile和​​。

1

要傳輸數據,您需要一個可供所有線程使用的字段。在你的情況下,它確實需要某種集合來處理多個條目。如果你創建了final這個字段,比如引用一個ConcurrentLinkedQueue,那麼你幾乎可以完成。該領域可以公開,每個人都可以看到它,或者你可以讓它與吸氣劑一起使用。

如果您使用非同步隊列,則需要做更多工作,因爲您必須手動同步對它的所有訪問,這意味着您必須跟蹤所有用法;有一個getter方法時不容易。您不僅需要保護隊列不受同時訪問,還必須確保相互依賴的調用最終位於同一個同步塊中。例如:

if (!queue.isEmpty()) obj = queue.remove(); 

如果整個事情是不同步的,queue完全能夠告訴你它不是空的,然後拋出一個NoSuchElementException當您嘗試獲得下一個元素。 (ConcurrentLinkedQueue的界面是專門設計用來讓你用一個方法調用這樣的操作,即使你不想使用它,也要仔細看看它。)

簡單的解決方案是將隊列包裝在另一個對象的方法都經過精心挑選全部同步。包裝的類,即使它是LinkedList或ArrayList,現在也會像CLQ一樣行事(如果你做得對),它可以自由地發佈到​​程序的其餘部分。

所以你必須什麼是真正的與不變(final)參照包裝類,其中包含一個LinkedList(例如),並同步使用LinkedList的存儲和訪問數據的方法一個全球性的領域。包裝類,如CLQ,將是線程安全的。

這可能需要一些變體。將包裝器與程序中的其他高級類組合起來可能很有意義。創建和提供嵌套類的實例也許是有意義的:可能只有一個添加到隊列中,而一個只能從隊列中刪除。 (您無法CLQ做到這一點。)

最後請注意:具有同步的一切,下一步就是要弄清楚如何取消同步(保持線程等待太多)不破壞線程安全。在這個上真的很努力,你最終會重寫ConcurrentLinkedQueue。