[英]How to Ensure Memory Visibility in Java when passing data across threads
我有一個生產者消費者喜歡模式,其中一些線程正在創建數據並定期傳遞放置的數據塊以供其他一些線程使用。
牢記Java內存模型,我如何確保傳遞給消費者線程的數據具有完全“可見性”?
我知道java.util.concurrent中的數據結構就像ConcurrentLinkedQueue那樣是專門為此而構建的,但是我希望盡可能低地使用這些數據結構而不使用它們,並且對封底下的內容有完全的透明度以確保記憶可視性部分。
如果你想要“低級”,那么看看volatile
和synchronized
。
要傳輸數據,您需要一個可供所有線程使用的字段。 在你的情況下,它確實需要某種集合來處理多個條目。 如果你讓這個領域成為final
,比如引用ConcurrentLinkedQueue,你就會完成任務。 該領域可以公開,每個人都可以看到它,或者你可以用吸氣劑使它可用。
如果使用非同步隊列,則還有更多工作要做,因為您必須手動同步對它的所有訪問,這意味着您必須跟蹤所有用法; 當有一個getter方法時不容易。 您不僅需要保護隊列不被同時訪問,還必須確保相互依賴的調用最終位於同一個同步塊中。 例如:
if (!queue.isEmpty()) obj = queue.remove();
如果整個事情沒有同步,那么queue
完全能夠告訴你它不是空的,然后當你試圖獲取下一個元素時拋出NoSuchElementException。 (ConcurrentLinkedQueue的界面專門設計用於通過一個方法調用來執行此類操作。即使您不想使用它,也要仔細查看它。)
簡單的解決方案是將隊列包裝在另一個對象中,該對象的方法經過精心選擇並且全部同步。 包裝類,即使它是LinkedList或ArrayList,現在將像CLQ一樣行動(如果你做對了),它可以自由地釋放到程序的其余部分。
因此,您將擁有一個真正的全局字段,其中包含對包含LinkedList(例如)的包裝類的不可變( final
)引用,並且具有使用LinkedList存儲和訪問數據的同步方法。 包裝類(如CLQ)將是線程安全的。
有些變體可能是可取的。 將包裝器與程序中的其他高級類組合可能是有意義的。 創建和創建嵌套類的實例也可能是有意義的:可能只添加到隊列中的一個和只從中刪除的一個。 (你無法用CLQ做到這一點。)
最后一點:已經同步了所有內容,下一步是弄清楚如何在不破壞線程安全的情況下進行不同步(以防止線程等待太多)。 在這方面工作非常努力,你最終將重寫ConcurrentLinkedQueue。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.