9

パターンが壊れていると見なされるのはなぜですか? それは私にはうまく見えますか?何か案は?

public static Singleton getInst() {
    if (instace == null) createInst();
    return instace;
}

private static synchronized createInst() {
     if (instace == null) {
         instace = new Singleton(); 
     }
}
4

7 に答える 7

21

一見問題ないように見えますが、この手法には多くの微妙な問題があるため、通常は避ける必要があります。たとえば、次の一連のイベントについて考えてみます。

  1. スレッドAは、値が初期化されていないことに気付いたため、ロックを取得して値の初期化を開始します。
  2. コンパイラによって生成されたコードは、Aが初期化の実行を完了する前に、部分的に構築されたオブジェクトを指すように共有変数を更新できます。
  3. スレッドBは、共有変数が初期化されている(または表示されている)ことに気づき、その値を返します。スレッドBは、値がすでに初期化されていると信じているため、ロックを取得しません。Aによって行われたすべての初期化がBによって認識される前に、Bがオブジェクトを使用すると、プログラムがクラッシュする可能性があります。

「volatile」キーワードを使用してシングルトンインスタンスを正しく処理することで、これを回避できます。

于 2010-09-01T10:05:18.107 に答える
11

議論全体は、脳の時間の膨大で無限の無駄です。99.9% の確率で、シングルトンには大きなセットアップ コストがかからず、不自然なセットアップで非同期の保証された遅延読み込みを実現する理由はまったくありません。

これは、Java でシングルトンを作成する方法です。

public class Singleton{
    private Singleton instance = new Singleton();
    private Singleton(){ ... }
    public Singleton getInstance(){ return instance; }
}

いっそのこと、それを列挙型にします:

public enum Singleton{
    INSTANCE;
    private Singleton(){ ... }
}
于 2010-09-01T10:39:23.623 に答える
7

壊れているかどうかはわかりませんが、かなり高価な同期のため、実際には最も効率的なソリューションではありません。より良いアプローチは、「Initialization On Demand Holder Idiom」を使用することです。これは、名前が示すように、最初に要求されたときにシングルトンをメモリにロードするため、遅延ロードになります。このイディオムで得られる最大の利点は、JLS によってクラスのロードが逐次的に行われることが保証されるため、同期する必要がないことです。

件名に関するウィキペディアの詳細なエントリ: http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

もう 1 つ覚えておくべきことは、Spring や Guice などの依存性注入フレームワークが出現したため、これらのコンテナーによってクラス インスタンスが作成および管理され、必要に応じてシングルトンが提供されるため、頭を悩ませる価値はありません。パターンの背後にあるアイデアを学びたい場合を除き、それは役に立ちます。また、これらの IOC コンテナーによって提供されるシングルトンはコンテナー インスタンスごとのシングルトンですが、通常はアプリケーションごとに 1 つの IOC コンテナーがあるため、問題にはなりません。

于 2010-09-01T09:59:14.063 に答える
6

問題は次のとおりです。JVMがコードを並べ替える可能性があり、フィールドが異なるスレッドで常に同じであるとは限りません。これを見てください:http ://www.ibm.com/developerworks/java/library/j-dcl.html 。volatileキーワードを使用するとこれを修正できるはずですが、Java1.5より前では壊れています。

ほとんどの場合、シングルチェックロックは十分に高速です。これを試してください。

// single checked locking: working implementation, but slower because it syncs all the time
public static synchronized Singleton getInst() {
    if (instance == null) 
        instance = new Singleton();
    return instance;
}

また、効果的なjavaもご覧ください。ここには、このトピックに関するすばらしい章があります。

これを要約すると、ダブルチェックロックを行わないでください。より良いイドムがあります

于 2010-09-01T10:28:19.680 に答える
4

初期化オンデマンドホルダーイディオム、うん、それだけです:

public final class SingletonBean{

    public static SingletonBean getInstance(){
        return InstanceHolder.INSTANCE;
    }

    private SingletonBean(){}

    private static final class InstanceHolder{
        public static final SingletonBean INSTANCE = new SingletonBean();
    }

}

Joshua Blochは、 Effective Javaの第2章の項目3で列挙型シングルトンパターンも推奨していますが、

// Enum singleton - the prefered approach
public enum Elvis{
    INSTANCE;
    public void leaveTheBuilding(){ ... }
}
于 2010-09-01T10:06:08.557 に答える
2

これはあなたの質問には答えませんが(他の人はすでに行っています)、シングルトン/遅延初期化オブジェクトに関する私の経験をお話ししたいと思います:

コードにはいくつかのシングルトンがありました。1 つのシングルトンにコンストラクター パラメーターを追加する必要があり、このシングルトンのコンストラクターがゲッターで呼び出されたため、深刻な問題が発生したことがあります。次の可能な解決策のみがありました。

  • このシングルトンを初期化するために必要なオブジェクトに静的ゲッター (または別のシングルトン) を提供します。
  • ゲッターのパラメーターとしてシングルトンを初期化するオブジェクトを渡すか、
  • インスタンスを渡すことでシングルトンを取り除きます。

最後に、最後のオプションは行く方法でした。ここで、アプリケーションの開始時にすべてのオブジェクトを初期化し、必要なインスタンスを (おそらく小さなインターフェイスとして) 渡します。この決定を後悔していません。

  • コードの依存関係が非常に明確であり、
  • 必要なオブジェクトのダミー実装を提供することで、コードをより簡単にテストできます。
于 2010-09-01T10:44:11.077 に答える
2

ここでの回答のほとんどは、それが壊れている理由については正しいですが、間違っているか、解決のための疑わしい戦略を示唆しています.

本当に、本当にシングルトンを使用する必要がある場合 (ほとんどの場合、テスト可能性を破壊し、クラスの構築方法に関するロジックをクラスの動作と組み合わせて、シングルトンを使用するクラスにシングルトンを取得する方法の知識を散らかしてはいけません) 、より脆弱なコードにつながる)、同期について懸念がある場合、正しい解決策は、静的初期化子を使用してインスタンスをインスタンス化することです。

private static Singleton instance = createInst();

public static Singleton getInst() {
    return instance ;
}

private static synchronized createInst() {
    return new Singleton(); 
}

Java 言語仕様では、クラスが初めてロードされるときに、静的イニシャライザーが 1 回だけ実行され、スレッドセーフな方法で実行されることが保証されています。

于 2010-09-01T13:54:38.840 に答える