簡體   English   中英

這個單例模式線程安全嗎?

[英]Is this singleton pattern thread safe?

我有一個單例服務器實例,我很好奇我的代碼是否是線程安全的。 我已經閱讀了不同的單例模式,我認為通常的方法是double-checked locking模式,如下所示:

public static Singleton getInstance() {
    if(singleton == null) {
        synchronized(Singleton.class) {
            if(singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

這被認為是設置/獲取單身的有效線程安全方式。 我讀過,獲取和設置單例的最簡單方法是lazy instantiation ,如下所示:

public static ClassicSingleton getInstance() {
    if(instance == null) {
        instance = new ClassicSingleton();
     }
     return instance;
}

現在我想知道的是我的變體是否是線程安全的。 我的代碼:

public static void startServer(int listeningPortNumber) throws IOException {
    if (server != null) {
        throw new IOException("Connection exists");
    }

    server = new Server(listeningPortNumber);
}

我的代碼與上面的惰性實例化模式非常相似,但我看不出我的代碼是如何不是線程安全的。 有沒有我沒看到的東西,或者這是真正有效的代碼?


參考: http//www.javaworld.com/article/2073352/core-java/simply-singleton.html

這不安全。

想象一下如果兩個線程同時調用startServer (或者足夠接近它)會發生什么:

  1. 線程A檢查server != null ,並看到該server為空 - 因此它不會拋出異常
  2. 線程B也是如此
  3. 線程A現在實例化new Server(listeningPortNumber);
  4. 線程B做同樣的事情,並且可能在第二次實例化時發生了不好的事情

如果server不是volatile ,問題就更糟了,因為你甚至不再需要交錯 - 線程A可能實例化new Server(...) ,但寫入server字段是'線程B看了很長時間(可能永遠),因為它沒有刷新到主存儲器。

但是,即使servervolatile ,由於交錯,該方法也很有效。

不,懶惰的單例模式不是線程安全的。

如果你想在Java中使用線程安全版本的Singleton模式,你應該實現Bill Pugh的解決方案。 代碼在這里:

public class OptimalSingleton
{
  protected OptimalSingleton()
  {
    // ...
  }

  private static class SingletonHolder
  {
    private final static OptimalSingleton INSTANCE = new OptimalSingleton();
  }

  public static OptimalSingleton getInstance()
  {
    return SingletonHolder.INSTANCE;
  }
}

關於SO的更多信息: Singleton模式(Bill Pugh的解決方案)

您的代碼不是線程安全的。

假設您有兩個線程,A和B,它們調用startServer(...) ,而在這些調用之前,服務器尚未初始化。 A傳遞一個值(a),B傳遞另一個值(b)到該方法。 您唯一知道的是,在每個線程中定義了操作的順序。

所以以下是可能的:

A: check server != null  (false)
B: check server != null  (false)
A: server = new Server(a)
B: server = new Server(b)

顯然,這違反了單身人士。 線程A將看到一個初始化為與傳入的值不同的值的服務器,並且從未拋出異常。

您的代碼不是線程安全的! 假設2個不同的線程同時調用startServer方法兩者都看到服務器變量為null所以將跳過if塊並且每個線程創建一個Server實例! 因此,您將在應用程序中以2個服務器實例結束。

暫無
暫無

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

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