簡體   English   中英

單例中的雙重檢查鎖定

[英]Double Checked Locking in Singleton

這是我的單例模式自定義類。 在這段代碼中,我使用如下雙重檢查鎖定。 當我在某些來源上閱讀了許多帖子時,他們說雙重檢查很有用,因為它可以防止同時運行的兩個並發線程生成兩個不同的對象。

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

我還是不太明白上面的代碼。 有什么問題,如果兩個線程在 instance 為 null 時一起運行同一行代碼?

if (searchBox == null) {
                synchronized (SearchBox.class) {
                    if (searchBox == null) {
                        searchBox = new SearchBox();
                    }
                }
            }

當那個出現。 兩個線程都會看到對象為空。 然后兩者同步。 然后,他們再次檢查,仍然看到它 null 並創建兩個不同的對象。 哎呀。

請為我解釋。 我理解錯了什么?

謝謝 :)

不,由於您正在獲取對SearchBox.class鎖定,因此一次只有一個線程會進入同步塊。 所以第一個線程進入然后發現searchBox為空並創建它然后離開同步塊,然后第二個線程進入塊然后它發現searchBox不為空,因為第一個線程已經創建了它所以它不會創建一個新的searchBox實例。

雙重檢查模式用於避免每次執行代碼時都獲得鎖定。 如果調用沒有同時發生,那么第一個條件將失敗,代碼執行將不會執行鎖定,從而節省資源。

讓我們看看這段代碼:

1 if (searchBox == null) {
2     synchronized (SearchBox.class) {
3     if (searchBox == null) {
4         searchBox = new SearchBox();
5     }
6 }

讓我們試着推理一下。 假設我們有兩個線程AB並假設其中至少一個到達第 3 行並觀察到searchBox == nulltrue 由於synchronized塊,兩個線程不能同時在第 3 行。 這是關鍵,理解為什么雙重檢查鎖定的工作方式。 因此,必須是AB首先通過synchronized 不失一般性,假設該線程是A 然后,當看到searchBox == null為真時,它會進入語句的主體,並將searchBox設置為SearchBox一個新實例。 然后它將最終退出synchronized塊。 現在輪到B進入:記住, B被阻塞等待A退出。 現在當它進入塊時,它會觀察searchBox 但是A將只剩下將searchBox設置為非null值。 完畢。

順便說一下,在 Java 中,實現單例的最好方法是使用單元素enum類型。 有效的Java

雖然這種方法尚未被廣泛采用,但單元素枚舉類型是實現單例的最佳方式。

僅當您擔心多個線程同時調用單例或一般情況下獲取鎖的成本時,才需要此雙重檢查鎖。

它的目的是防止不必要的同步,從而使您的代碼在多線程環境中保持快速。

查看此鏈接了解更多信息。

如果您在 Java 1.5 或更高版本中運行,並且您在雙重檢查鎖定機制中使用volatile關鍵字,它會正常工作。 當您使用volatile關鍵字時,根據上面的相同鏈接,您的示例沒有被破壞。

if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. 如果已經創建了一個實例,不要做任何事情 - 避免鎖定線程
  2. 獲得鎖的第一個線程檢查並發現沒有這樣的對象並創建它。 它釋放鎖,第二個可以做同樣的事情——它必須檢查對象是否存在,因為第一個可能已經創建了它。

所以基本上外部if用於防止冗余鎖 - 它讓所有線程知道已經有一個對象並且他們不需要鎖定/做任何事情。 內部if用於讓並發線程知道另一個線程是否已經創建了該對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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