5

このコードは、Java の二重チェック ロックの問題を解決しますか?

public class DBAccessService() {
    private static DBAccessService INSTANCE;  

    private DBAccessService() {}

    public static DBAccessService getInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        return createInstance();
    }

    private static synchronized DBAccessService createInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        DBAccessService instance = new DBAccessService();
        INSTANCE = instance;

        return INSTANCE;
    }
}

注意すべき点が 2 つあります。

  1. getInstance()は同期されないため、INSTANCE が初期化された後、同期のコストはかかりません
  2. createInstance()同期されています

問題は、このコードに何か問題があるかどうかです。それは合法で、常にスレッドセーフですか?

4

5 に答える 5

8

INSTANCEあなたはそれが機能するように宣言する必要がありますvolatile

private static volatile DBAccessService INSTANCE;

Java5以降でのみ機能することに注意してください。「ダブルチェックされたロックが壊れている」宣言を参照してください。

于 2011-05-12T09:49:04.207 に答える
4

この特定の問題を解決するために、実際の Java 並行性(基本的に java.util.concurrent ライブラリを作成したチームによって作成された) は、Lazy Initialization ホルダー クラスのイディオム(私のコピーの 348 ページ、16.7 ではなくリスト 16.6 ) を推奨しています。

@ThreadSafe
public class DBAccessServiceFactory {
  private static class ResourceHolder {
    public static DBAccessService INSTANCE = new DBAccessService();
  }
  public static DBAccessService getResource() {
    return ResourceHolder.INSTANCE;
  }
}

これは常に合法であり、スレッドセーフです。私は専門家ではないので、これがあなたのコードより優れているとは言えません。ただし、Doug Lea と Joshua Bloch が推奨するパターンであることを考えると、間違いを犯しやすいので、私は常にあなたや私が発明したコードよりもこのパターンを使用します (この質問に対する間違った回答の数が示すように)。 )。

彼らが言う不安定な問題に関連して:

JMM (Java 5.0 以降) のその後の変更により、リソースが揮発性にされた場合に DCL が機能するようになりました...ただし、遅延初期化ホルダーのイディオムは同じ利点を提供し、理解しやすくなっています。

于 2011-05-12T10:10:06.370 に答える
2

この記事では、別のシングルトン クラスを使用する場合、「ダブル チェック ロギング」は問題にならないと主張されています。

public class DBAccessHelperSingleton {
    public static DBAccessHelper instance = new DBAccessHelper(); 
} 

同じ利点があります。フィールドは、最初に参照される前にインスタンス化されません。

前述のように、volatileJDK >= 5 のみをターゲットにしている場合は、そのままにしておく必要があります。

于 2011-05-12T09:55:53.307 に答える
1

これはスレッド セーフではありません。優れた記事を確認してください。1 つのスレッドが DbAccessService の完全に初期化されたインスタンスを参照するという保証はありません。シンプルを使用しない理由

public class DBAccessService() {
     public static DBAccessService INSTANCE = new DBAccessService();
}
于 2011-05-12T09:58:56.740 に答える
0

うまく見えます。2つのスレッドがgetInstance()を呼び出し、INSTANCEが初期化されていない場合、1つのスレッドのみがcreateInstance()を続行でき、2番目のスレッドはインスタンスがnullではないことをすでに認識しています。

唯一のものはvolatileINSTANCEのキーワードです。それ以外の場合、Javaはそれをキャッシュできます。

于 2011-05-12T09:50:02.667 に答える