10

私はインスタンスをキャッシュし、それらを使用するときにそれらを複製するこのクラスを持っています (データは変更可能です)。

これで並べ替えの問題に直面できるのだろうか。

この回答と JLSを見てきましたが、まだ自信がありません。

public class DataWrapper {
    private static final ConcurrentMap<String, DataWrapper> map = new ConcurrentHashMap<>();
    private Data data;
    private String name;

    public static DataWrapper getInstance(String name) {
        DataWrapper instance = map.get(name);
        if (instance == null) {
            instance = new DataWrapper(name);
        }
        return instance.cloneInstance();
    }

    private DataWrapper(String name) {
        this.name = name;
        this.data = loadData(name);  // A heavy method
        map.put(name, this);  // I know
    }

    private DataWrapper cloneInstance() {
        return new DataWrapper(this);
    }

    private DataWrapper(DataWrapper that) {
        this.name = that.name;
        this.data = that.data.cloneInstance();
    }
}

私の考え:DataWrapperランタイムは、オブジェクトを初期化する前に、コンストラクターでステートメントを並べ替えて、現在のインスタンスを発行する(マップに入れる) ことができdataます。2 番目のスレッドDataWrapperがマップからインスタンスを読み取り、nulldataフィールド (または部分的に構築された) を確認します。

これは可能ですか?はいの場合、それは参照エスケープのみによるものですか?

そうでない場合は、事前発生の一貫性をより簡単に説明する方法を説明していただけますか?

私がこれをした場合:

public class DataWrapper {
    ...
    public static DataWrapper getInstance(String name) {
        DataWrapper instance = map.get(name);
        if (instance == null) {
            instance = new DataWrapper(name);
            map.put(name, instance);
        }
        return instance.cloneInstance();
    }
    
    private DataWrapper(String name) {
        this.name = name;
        this.data = loadData(name);     // A heavy method
    }
    ...
}

それはまだ同じ問題を起こしやすいですか?

複数のスレッドが同時に同じ値のインスタンスを作成して配置しようとした場合に、1 つまたは 2 つの余分なインスタンスが作成されてもかまわないことに注意してください。

編集:

name フィールドと data フィールドが final または volatile の場合はどうなるでしょうか?

public class DataWrapper {
    private static final ConcurrentMap<String, DataWrapper> map = new ConcurrentHashMap<>();
    private final Data data;
    private final String name;
    ... 
    private DataWrapper(String name) {
        this.name = name;
        this.data = loadData(name);  // A heavy method
        map.put(name, this);  // I know
    }
    ...
}

まだ危険ですか?私の知る限り、コンストラクターの初期化の安全性の保証は、初期化中に参照がエスケープされていない場合にのみ適用されます。これを確認する公式の情報源を探しています。

4

2 に答える 2