4

このトピックに基づいて、実装がAtomicIntegersに基づいているシングルトンパターンの興味深いバージョンを作成しました。

質問は次のとおりです。

  • この実装は正しく、スレッドセーフですか。一般に、スレッドの同期と管理にアトミック変数を使用することは可能ですか。
  • volatile追加の質問:この実装がスレッドセーフである場合、インスタンス変数の修飾子が本当に必要ですか?
public class StrangeSingleton
{

    private StrangeSingleton() {};

    private static volatile Object instance;

    private static AtomicInteger initCounter = new AtomicInteger();
    private static AtomicInteger readyCounter = new AtomicInteger();

    static Object getInstance()
    {

        if (initCounter.incrementAndGet() == 1)
        {
            instance = new Object();

            readyCounter.incrementAndGet();

            return instance;
        }
        else if (readyCounter.get() == 1)
        {
            return instance;
        }
        else
        {
            //initialization not complete yet.
            //write here some logic you want:               
            //sleep for 5s and try one more time,
            //or throw Exception, or return null..

            return null;
        }
    }
}

更新:プライベートコンストラクターを追加しましたが、それは重要ではありません。

4

4 に答える 4

8

この実装は正しく、スレッドセーフですか?また、スレッドの同期と管理にアトミック変数を使用することは一般的に可能ですか?

ただし、変更に迅速に対応するために忙しく待機する必要があるため、通常はより複雑で CPU を集中的に使用します。

追加の質問: この実装がスレッドセーフである場合、インスタンス変数に volatile 修飾子が本当に必要ですか?

この場合、AtomicInteger には volatile フィールドが含まれているため、正しい発生前/発生後の動作が保証されます。


もちろん、スレッドセーフではるかに単純な列挙型を使用することもできます;)

enum Singleton {
    INSTANCE;
}
于 2012-09-19T13:28:17.397 に答える
2

この実装は正しく、スレッドセーフですか?一般的に、スレッドの同期と管理にアトミック変数を使用できますか?

はい。ただし、readyCounter変数にはおそらく次のようにCountDownLatchを使用する必要があります。

private static AtomicInteger initCounter = new AtomicInteger();
private static CountDownLatch readyCounter = new CountDownLatch(1);

static Object getInstance()
{

    if (initCounter.incrementAndGet() == 1)
    {
        try {
            instance = new Object();
            return instance;
        } finally {
            readyCounter.countDown();
        }
    }
    else
    {
        readyCounter.await();
        return instance;
    }
}

await()を呼び出すと、初期化の競合状態も解決されます。(コンストラクター例外でのデッドロックを回避するために、try-finally ブロックも追加しました。)

追加の質問: この実装がスレッドセーフである場合、インスタンス変数に volatile 修飾子が本当に必要ですか?

いいえ、インスタンス変数にアクセスする前に関連する関数AtomicIntegerまたは関数を呼び出す場合は違います。ドキュメント事前発生をCountDownLatch探します。

于 2012-09-19T13:47:35.607 に答える
0

私はむしろsynchronizedあなたのメソッドで1つのブロックを実行したいと思いますgetInstance()。これで十分です。@Davidが気付いたほど安全ではないこれらの奇妙なカウン​​ターは必要ありません。

于 2012-09-19T14:10:37.890 に答える
0

スレッドT1は で中断される可能性がありinstance = new Object();T2はまだインクリメントされていないelse{}ため、ブロックにヒットします。readyCounter初期化完了したため、正確ではありません。遅れているのは、状態の簿記です。

于 2012-09-19T13:48:51.040 に答える