簡體   English   中英

雙重檢查鎖定而不創建對象

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM