1
public static Singleton getInstance()
{ 
if (instance == null)
    {
    synchronized(Singleton.class) {  //1
    if (instance == null)          //2
    instance = new Singleton();  //3
        }
    }
    return instance; //4
} 

上記のコードで、10 個のスレッドがこのメソッドを呼び出しているとします。すべてのスレッドが最初の if 条件を超えた後、1 つのスレッドが同期ブロックに入り、インスタンスを作成します。残りの 9 つのスレッドは、インスタンスが作成されたとしても、同期ブロックを通過する必要があります。いずれかのスレッドがシングルトン インスタンスを作成するとすぐに、他のすべてのスレッドは待機しません。これに対する解決策があれば教えてください。

4

4 に答える 4

0

あなたの設計では、残りのスレッドは実際に一度に 1 つのスレッドを通過する必要があります。実際、このようなロック戦略では、同じ結果が得られます。とはいえ、ロックのコストは今日では取るに足らないものです。

示されていませんが、このイディオムが機能instanceするには変数が必要であることに注意してください。volatile

求めていることを行うパターン (低コストの遅延初期化) が必要な場合は、代わりに静的初期化方法をお勧めします (Brian Goetz が推奨):

public class Singleton {
    static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }

このアプローチは合理的で整頓されており、スレッドセーフでもあります。あなたの場合、静的getInstanceメソッド(または実際には任意の静的メソッド)にアクセスする最初のスレッドは初期化のコストを支払いますが、後続のスレッドはすべてブロックしません。

于 2013-02-27T09:25:05.030 に答える
0

シングルトンが非常に複雑な構造化クラスではないか、コンストラクターの初期化でロードするものがあまりないことを考えると、その良いコードとブロッキング時間はそれほど長くないと思います。すべてのスレッドが同時に発生した場合、いずれかのスレッドによって最初のオブジェクトが作成されるまで待機する必要があります。

于 2013-02-27T06:54:52.343 に答える
0

条件付き同期はありません。同期を使用しているときはいつでも、それが常に適用されることを意味します。上記の例では、もちろん、静的ブロックでシングルトンを初期化するオプションになります。その場合、ゲッター用の同期ブロックは必要ありません。

于 2013-02-27T06:55:15.733 に答える
0

一度に 1 つずつロックを取得せずに、他の 9 つのスレッドすべてを実行したいと言っているのですか。

最初に、初期化を待った後にロックを取得することによるパフォーマンスの問題はごくわずかであることに言及しなければなりません。第二に、シングルトンは悪であり、それらを実装するためのより良い方法があります。

ただし、ロックを部分的に再実装したい場合は、これを行うことができます。これを行うための既製のものがあるかもしれませんがFuture、何も見えません。とにかく、私の頭の上からの悪い実装:

private static final AtomicReference<Object> ref = new AtomicReference<>();
    // <Object> as we require two distinguished values. :(
    // (I guess could use a mark.)

public static Singleton getInstance() {
    for (;;) { // May want to play with park to avoid spinning.
        Object obj = ref.get();
        if (obj instanceof Singleton) {
           return (Singleton)obj;
        }
        if (ref.weakCompareAndSet(null, Thread.currentThread())) {
            Singleton instance = null; // To reset on fail.
            try {
                instance = new Singleton();
            } finally {
                ref.set(instance);
            }
            return instance;
        }
    }
}

例外がないと仮定すると、複雑になりすぎずに少し改善できると思います。

    {
        Object obj = ref.get();
        if (obj instanceof Singleton) {
           return (Singleton)obj;
        }
    }

    if (ref.compareAndSet(null, Thread.currentThread())) {
        Singleton instance = new Singleton();
        ref.set(instance);
        return instance;
    }

    for (;;) {
        Object obj = ref.get();
        if (obj instanceof Singleton) {
           return (Singleton)obj;
        }
    }
于 2013-02-27T07:33:22.577 に答える