[英]Double-Checked Locking without creating objects
我正在使用雙重檢查鎖定(DCL)來避免在不需要的情況下對對象進行同步。 就我而言,當某個緩沖區為空時,我需要進行同步,以使“處理線程”等待“傳遞線程”再次通知它-否則,“處理線程”將循環運行而無需執行任何有用的操作。
兩個線程共享這些對象:
Object bufferLock = new Object();
Queue<Command> commands = new ConcurrentLinkedQueue<>(); // thread safe!
線程1(“交付線程”-填充緩沖區):
while (true)
Command command = readCommand();
commands.add(command);
synchronize (bufferLock){
bufferLock.notify(); // wake up Thread 2 (if waiting)
}
}
線程2(“處理線程”-清空緩沖區):
while (true){
if (commands.peek() == null){ // not creating anything here
synchronized (bufferLock){
if (commands.peek() == null){ // also not creating anything
bufferLock.wait();
}
}
}
Command command = commands.poll();
processCommand(command);
}
現在,NetBeans正在顯示有關DCL的警告,這使我更深入地研究了這個主題,因為DCL的概念對我來說是未知的-我只是開始自己使用它。
據我從網上閱讀的幾篇文章了解到,使用此模式時存在一個Java錯誤,但是所有示例都將其與延遲加載結合使用。 在這些示例中,對象是在同步塊內創建的。 在我的同步代碼中,我沒有創建對象。
我的代碼不安全嗎? NetBeans顯示警告是否正確? 請注意,NetBeans之前有一個與DCL相關的錯誤 ,因此我有些困惑。
您創建的模式(或更確切地說,是反模式!)並不嚴格構成Double Checked Locking,這通常是指對象引用從null開始然后由需要引用該對象的第一個線程實例化的情況。僅在空檢查之后才進行同步。 在Java 5之前,您不能嚴格說來用Java正確實現這一點(盡管由於大多數JVM的實現方式,您可能會意外地放棄它)。 從Java 5開始,您可以使用它,但是本質上是毫無意義的。 (您可能對我不久前寫的有關Java中的雙重檢查鎖定的文章感興趣,該文章更詳細地介紹了此問題。類加載器實際上具有內置的同步功能,可在極少數情況下確實需要諸如DCL)。
現在,順便說一句。 嚴格來講,您在這里所擁有的並不是DCL。
您確實遇到的問題是您正在嘗試混合范例 。 Java並發庫的存在理由通常是避免使用同步和等待/通知進行低級鎖定。 因此,您真正應該做的就是簡單地使用BlockingQueue的某種形式,並使用其內置的阻塞行為。 同樣,在其他示例中,我可能還請您參考我在阻塞隊列中編寫的一些材料 。
請檢查Brian Goetz撰寫的“ Java Concurrency in Practice”一書中的第16.2.4節“雙重檢查鎖定”。 它解釋了為什么DCL是錯誤的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.