本「Java Concurrency in Practice」では、次のコードはスレッドセーフではないことが言及されています。
@NotThreadSafe
public class DoubleCheckedLocking {
private static Resource resource;
public static Resource getInstance(){
if(resource == null){
synchronized (DoubleCheckedLocking.class){
if(resource == null)
resource = new Resource();
}
}
return resource;
}
}
次の理由により、スレッドセーフではありません: - 1 つのスレッドがリソースの新しいインスタンスを作成できる - 「if」条件で同時に別のスレッドが空の参照を取得できないが、リソースのオブジェクトが完全に初期化されない
この質問には同様のコードがあります。リソースは concurentHashMap に格納され、人々はそれがスレッドセーフであると言います。このようなもの:
public class DoubleCheckedLocking2 {
private static ConcurrentHashMap<String, ComplexObject> cache = new ConcurrentHashMap<String, ComplexObject>();
public static ComplexObject getInstance(String key) {
ComplexObject result = cache.get(key);
if (result == null) {
synchronized (DoubleCheckedLocking2.class) {
ComplexObject currentValue = cache.get(key);
if (currentValue == null) {
result = new ComplexObject();
cache.put(key, result);
} else {
result = currentValue;
}
}
}
return result;
}
}
ConcurrentHashMap に値を格納するとコードが threadSafe になるのはなぜですか? ComplexObject が完全に初期化されず、この「部分オブジェクト」がマップに保存される可能性はまだあると思います。また、他のスレッドは、完全に初期化されていない部分的なオブジェクトを読み取ります。
「前に起こる」とは何かを知っていると思います。JDK 8.0_31 のコードを分析しましたが、まだ答えがわかりません。
computeIfAbsent、putIfAbsentなどの関数を認識しています。このコードは別の方法で記述できることを知っています。このコードをスレッドセーフにする詳細を知りたくないだけです。