Javaシングルトンと同期


117

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

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

回答:


211

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

ドラコニアン同期:

private static YourObject instance;

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

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

同期の再確認

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 によって制御されるため、追加の同期は必要ありません。


23
警告-ダブルチェック同期に注意してください。メモリモデルの「問題」が原因で、Java 5以前のJVMでは正しく機能しません。
スティーブンC

3
-1 Draconian synchronizationおよびDouble check synchronizationgetInstance()メソッドは静的でなければなりません!
グリム

2
@PeterRaderである必要ありません、そうstaticである場合はより理にかなっている可能性があります。要求に応じて修正。
ジェフリー

4
あなたはダブルチェックされたロックの実装が機能することは保証されていません。これは、ダブルチェックロックについて引用した記事で実際に説明されています。:) 1.5以上で適切に動作するvolatileを使用する例があります(ダブルチェックロックは1.5以下では単純に壊れています)。記事で引用されている初期化オンデマンドホルダーは、おそらくあなたの答えのより簡単な解決策でしょう。
stuckj

2
@MediumOne AFAIK、r正確さのために必要ではありません。ローカル変数にアクセスするよりもはるかにコストがかかるため、揮発性フィールドへのアクセスを回避するための最適化にすぎません。
ジェフリー

69

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

public class MySingleton {

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

     private MySingleton () {}

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

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

まるで魔法のようです。

それは実際にはジュルタドの列挙型パターンに非常に似ていますが、列挙型パターンは列挙型概念の乱用であることがわかります(ただし、機能します)


11
同期はまだ存在しており、プログラマーではなくJVMによって強制されます。
ジェフリー

@ジェフリーあなたは正しいです-私はすべてを入力していました(編集を参照)
ボヘミアン

2
私はそれがJVMに違いをもたらさないことを理解しています、私は自己文書化されたコードが行く限りそれが私に違いをもたらしたと言っています。これまで(または列挙型)の「final」キーワードなしでJavaですべての大文字を確認したことはありませんが、認知的不協和音が少しありました。Javaをフルタイムでプログラミングしている人にとっては、おそらく違いはありませんが、言語を前後にジャンプする場合は、明示的にすることが役立ちます。初心者のための同上。ただし、このスタイルにすぐに適応できると思います。おそらくすべて大文字で十分です。私はあなたの投稿が好きでした
Rubyの

1
正解ですが、一部はわかりませんでした。「さらに、クラスローダーは、クラスにアクセスする前にすべての静的初期化が完了することを保証します-それがスレッドの安全性を提供するものです。」、それがスレッドセーフにどのように役立つか、私は少し混乱しています。
gaurav jain 2014

2
@ wz366実際には必要ありませんが、スタイル上の理由で同意します(他のコードがアクセスできないため、これは事実上最終なので)final追加する必要があります。できました。
ボヘミアン

21

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

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

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

Singleton.SINGLE.myMethod();

8

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

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

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


メソッド全体を同期させるのではなく、オブジェクト作成コードをSynchronizedブロックに配置することで役立ちますか?
RickDavis

@RaoGいいえ。同期ブロックでチェック作成の両方が必要です。これら2つの操作を中断せずに一緒に実行する必要があります。そうしないと、上記で説明した状況が発生する可能性があります。
Oleksi

7

列挙型シングルトン

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

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 ;
  }
}

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

シングルトンの早期ロード(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");
  }

}

2

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

public class MySingleton {

  private static final MySingleton instance;

  static {
     instance = new MySingleton();
  }

  private MySingleton() {
  }

  public static MySingleton getInstance() {
    return instance;
  }

}

@Vimshaいくつか他のもの。1. instanceファイナルにする2. getInstance()スタティックにする。
John Vint 14年

シングルトンでスレッドを作成する場合はどうしますか。
アルンジョージ

@ arun-georgeは、スレッドプール、必要に応じて単一のスレッドプールを使用し、どのエラーが発生してもスレッドが停止しないようにするには、while(true)-try-catch-throwableで囲みますか?
tgkprog 2015

0

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

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

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

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

これは、メソッドの実装方法によって異なります。揮発性変数なしで二重ロックを使用すると、部分的に構築されたシングルトンオブジェクトが取得される場合があります。

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

このダブルチェックロックの例でvolatileが使用される理由

シングルトンのgetInstance()を同期化できますか?

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

以下の方法でシングルトンを実装する場合は不要

  1. 静的初期化
  2. 列挙型
  3. Initialization-on-demand_holder_idiomによるLazyInitalaization

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

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


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

ソース:効果的なJava->アイテム2

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.