2

これが私のシングルトンクラスです。

staticinstanceフィールドは揮発性ではないため、並べ替え/可視性の問題が発生します。それを解決するために、インスタンスvalフィールドは final になります。インスタンスは適切に構築されているため、インスタンスが表示される場合、クライアントは常にvalフィールドが初期化されていることを確認する必要があります。

    static class Singleton {
    private static Singleton instance;
    private final String val;
    public Singleton() { this.val = "foo"; }

    public static Singleton getInstance() {
        if (instance == null)
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
            }
        }
        return instance;
    }
    public String toString() { return "Singleton: " + val; }
}

ただし、別の問題があります-「インスタンス」フィールドの保護されていない読み取りが2つあり、クライアントが実際の値ではなくnullを取得できるように並べ替える(?)ことができます:

public static Singleton getInstance() {
    Singleton temp = instance;
    if (instance != null) return temp;
    else { /* init singleton and return instance*/ }
}

これを回避するには、ローカル変数を導入できるように感じます。

public static Singleton getInstance() {
    Singleton temp = instance;
    if (temp == null)
        synchronized (Singleton.class) {
            if(instance == null) {
                instance = new Singleton();
                temp = instance;
        }
    }
    return temp;
}

保護されていない値の読み取りは 1 つしかないため、これで問題が解決したように見えます。しかし...シングルスレッドのセマンティクスを変更せずに(ほとんど?)プログラムフローを変更しました。これは、この変換は安全であり、volatile との適切な事前発生関係を確立せずにこのコードを機能させる方法がないため、コンパイラが私の回避策を元に戻すことができるということですか?

4

3 に答える 3

1

同じ変数の読み取りの並べ替えが実際に発生するかどうかはわかりませんが、ローカル変数が他のスレッドのアクティビティの影響を受けないことは保証されています。そのような読み取りの並べ替えが発生しない場合でも、この保証は、読み取り中に同時に更新される可能性のあるすべての変数に関連しています。値を読み取り、それをローカル変数に保存すると、ローカル変数の値が確実に更新されます。その後急に変化しないこと。もちろん、値が参照である場合、その保証は参照されるオブジェクトのフィールドには適用されません。

関連する文は、JLS §17.4.1にあります。

ローカル変数 (§14.4)、正式なメソッド パラメーター (§8.4.1)、および例外ハンドラー パラメーター (§14.20) は、スレッド間で共有されることはなく、メモリ モデルの影響を受けません。

したがって、答えはノーです。コンパイラは、ローカル変数を導入するという回避策を元に戻すことはできません。

于 2014-09-02T18:05:50.977 に答える
0

遅延初期化シングルトンを行う最も安全な方法は、別のクラスを使用して単一インスタンス フィールドを保持し、Java 言語がクラスの初期化に提供する保証に依存することです。

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

  public Singleton getInstance() {
    return Holder.instance;
  }
}

Holderクラスは、最初に呼び出されたときにのみ初期化されます (したがって、インスタンスが作成されます) getInstance()

于 2014-08-22T09:17:33.190 に答える