web-dev-qa-db-ja.com

Javaシングルトンと同期

シングルトンとマルチスレッドに関する私の質問を明確にしてください:

  • マルチスレッド環境でJavaでシングルトンを実装する最良の方法は何ですか?
  • 複数のスレッドがgetInstance()メソッドに同時にアクセスしようとするとどうなりますか?
  • シングルトンのgetInstance()synchronizedを作成できますか?
  • シングルトンクラスを使用する場合、同期は本当に必要ですか?
106
RickDavis

はい、必要です。遅延初期化でスレッドセーフを実現するために使用できる方法はいくつかあります。

ドラコニア同期:

private static YourObject instance;

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

このソリューションでは、everyスレッドを同期する必要がありますが、実際には最初の数個だけを同期する必要があります。

二重チェック同期

private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}

このソリューションは、シングルトンを取得しようとする最初の数スレッドのみがロックを取得するプロセスを通過するようにします。

オンデマンドの初期化

private static class InstanceHolder {
    private static final YourObject instance = new YourObject();
}

public static YourObject getInstance() {
    return InstanceHolder.instance;
}

このソリューションは、クラスの初期化に関するJavaメモリモデルの保証を利用して、スレッドの安全性を確保します。各クラスは一度しかロードできず、必要なときにのみロードされます。つまり、最初にgetInstanceが呼び出され、InstanceHolderがロードされ、instanceが作成されます。これはClassLoadersによって制御されるため、追加の同期は不要です。

198
Jeffrey

このパターンは、インスタンスのスレッドセーフな遅延初期化を行いますwithout明示的な同期!

public class MySingleton {

     private static class Loader {
         static final MySingleton INSTANCE = new MySingleton();
     }

     private MySingleton () {}

     public static MySingleton getInstance() {
         return Loader.INSTANCE;
     }
}

これは、クラスローダーを使用してすべての同期を無料で行うために機能します。クラスMySingleton.LoadergetInstance()メソッド内で最初にアクセスされるため、getInstance()が初めて呼び出されたときにLoaderクラスがロードされます。 。さらに、クラスローダーは、クラスにアクセスする前にすべての静的初期化が完了することを保証します。これがスレッドセーフを実現します。

それは魔法のようなものです。

実際にはJhurtadoの列挙型パターンに非常によく似ていますが、列挙型パターンは列挙型の概念の乱用であることがわかります(動作しますが)

64
Bohemian

Javaのマルチスレッド環境で作業しており、それらのすべてのスレッドがクラスの単一インスタンスにアクセスしていることを保証する必要がある場合は、Enumを使用できます。これには、シリアル化の処理を支援するという追加の利点があります。

public enum Singleton {
    SINGLE;
    public void myMethod(){  
    }
}

そして、次のようにスレッドにインスタンスを使用させるだけです:

Singleton.SINGLE.myMethod();
21
jhurtado

はい、getInstance()を同期させる必要があります。そうでない場合、クラスの複数のインスタンスを作成できる状況が発生する可能性があります。

getInstance()を同時に呼び出す2つのスレッドがある場合を考えます。ここで、T1がinstance == nullチェックを過ぎて実行され、T2が実行されるとします。この時点では、インスタンスは作成または設定されていないため、T2はチェックに合格してインスタンスを作成します。ここで、実行がT1に戻ると想像してください。これでシングルトンが作成されましたが、T1はすでにチェックを行っています!再びオブジェクトを作成します! getInstance()を同期すると、この問題を防ぐことができます。

シングルトンをスレッドセーフにする方法はいくつかありますが、getInstance()同期化するのがおそらく最も簡単です。

8
Oleksi

列挙型シングルトン

スレッドセーフなシングルトンを実装する最も簡単な方法は、Enumを使用することです

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

このコードは、Java 1.5にEnumが導入されてから機能します。

ダブルチェックロック

マルチスレッド環境(Java 1.5以降)で動作する「クラシック」シングルトンをコーディングする場合は、これを使用する必要があります。

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

Volatileキーワードの実装が異なるため、これは1.5より前のスレッドセーフではありません。

シングルトンの早期ロード(Java 1.5より前でも動作します)

この実装は、クラスがロードされるとシングルトンをインスタンス化し、スレッドセーフを提供します。

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
6
Dan Moldovan

静的コードブロックを使用して、クラスのロード時にインスタンスをインスタンス化し、スレッド同期の問題を防ぐこともできます。

public class MySingleton {

  private static final MySingleton instance;

  static {
     instance = new MySingleton();
  }

  private MySingleton() {
  }

  public static MySingleton getInstance() {
    return instance;
  }

}
2
usha

マルチスレッド環境でJavaでシングルトンを実装する最良の方法は何ですか?

シングルトンを実装する最良の方法については、この投稿を参照してください。

Javaでシングルトンパターンを実装する効率的な方法は何ですか?

複数のスレッドが同時にgetInstance()メソッドにアクセスしようとするとどうなりますか?

メソッドを実装した方法に依存します。volatile変数なしでダブルロックを使用すると、部分的に構築されたシングルトンオブジェクトを取得できます。

詳細については、この質問を参照してください。

二重チェックロックのこの例で揮発性が使用される理由

シングルトンのgetInstance()を同期させることはできますか?

シングルトンクラスを使用する場合、同期は本当に必要ですか?

以下の方法でシングルトンを実装する場合は必要ありません

  1. 静的初期化
  2. 列挙型
  3. Initialize-on-demand_holder_idiomを使用したLazyInitalaization

詳細については、この質問を参照してください

Javaシングルトンデザインパターン:質問

0
Ravindra babu
public class Elvis { 
   public static final Elvis INSTANCE = new Elvis();
   private Elvis () {...}
 }

ソース:有効Java->アイテム2

クラスが常にシングルトンのままであることが確実な場合は、使用することをお勧めします。

0
Ashish