1

コンストラクターが完全に実行されていなくても、参照が null ではない読み取りを行う JVM が続く一部のメモリ モデルが原因で、シングルトンの二重チェック メカニズムが失敗することを読みました。

以下のコードのコンストラクター内で時間のかかる操作を行って同じことをテストしようとしましたが、それでも正常に動作しているようです。

public class Singleton {
private static Singleton singleton;
private Integer i =  0;
private Singleton() {

    for(long j = 0; j<99999999; j++){
        double k = Math.random();
        k= k+1;
    }
    i = 10;
}

private static Singleton getSinglton() {
    if(singleton == null){
        synchronized (Singleton.class) {
            if(singleton == null){
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

public static void main(String[] args) {
    Runnable runnable1 = new Runnable() {

        @Override
        public void run() {
            Singleton singleton = Singleton.getSinglton();
            System.out.println(singleton.getI());
        }
    };
    Thread t1 = new Thread(runnable1);
    Thread t2 = new Thread(runnable1);
    Thread t3 = new Thread(runnable1);
    Thread t4 = new Thread(runnable1);
    Thread t5 = new Thread(runnable1);
    Thread t6 = new Thread(runnable1);
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
    t6.start();
}

public void setI(Integer i) {
    this.i = i;
}

public Integer getI() {
    return i;
}

}

私が得る結果は 10 10 10 10 10 10 です

10 ではなく値 0 を読み取るスレッドはほとんどないと予想していましたが、値が 10 として正しく読み取られるたびに、Java SE-1.6 で同じものを使用しているため、問題は解決しましたか?

4

2 に答える 2

1

まず、DSL を使用しないでください。壊れているからではなく、必要以上に複雑だからです。

enum Singleton {
    INSTANCE;
}

これははるかに単純であるだけでなく、 getInstance() が行うチェックを必要としないため高速でもあります。

次に、DSL は 9 年前に Java 5.0 のメモリ モデルで修正されました。

最後に、モデルが壊れていたとしても、特定のバージョンの Java でモデルが表示されるという保証はありませんでした。すべてのバージョンの Java で動作することが保証されているわけではありません。Java の Sun バージョンは、JLS で十分にカバーされていない問題を修正する傾向がありました。

于 2013-10-06T13:02:00.003 に答える
0

まず、Peter Lawrey が指摘したように、ダブルチェック ロックを使用する理由はまったくありません。

もう 1 つのポイントは、Java メモリ モデルが Java 5 で変更されたため、finalインスタンス フィールドでインスタンスを不変にするか、シングルトン holder を宣言することで、ダブルチェック ロック コードを修正できるようになったことですvolatile

どちらも行っていないため、コードはデータ競合の可能性がありますが、テスト時にそれが見られることを意味するものではありません。適切な同期なしで共有変数にアクセスするときに初期化されていないインスタンスが表示される理由は、ヒープ変数への読み取りと書き込みの順序が変更されるためです。しかし、JVM は単に楽しみのためにこれらの操作を並べ替えるわけではありません。パフォーマンスを向上できると判断した場合に行います。あなたのコード例では、それはほとんどありません。

しかし、当てにしないでください…</p>

于 2013-10-08T08:56:11.593 に答える