Immutability and reordering

Der folgende Code (Java Concurrency in Practice Listing 16.3) ist aus offensichtlichen Gründen nicht threadsicher:

public class UnsafeLazyInitialization {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null)
            resource = new Resource();  // unsafe publication
        return resource;
    }
}

Einige Seiten später, in Abschnitt 16.3, heißt es jedoch:

UnsafeLazyInitialization ist eigentlich sicherob Resource ist unveränderlich.

Ich verstehe diese Aussage nicht:

ObResource ist unveränderlich, jeder Thread beobachtet dasresource Die Variable wird entweder als null oder vollständig konstruiert angezeigt (dank der starken Garantie der endgültigen Felder, die vom Java-Speichermodell bereitgestellt werden).Nichts verhindert jedoch eine Neuordnung der Befehle: insbesondere die beiden Lesevorgänge vonresource könnte nachbestellt werden (es gibt eine Lektüre in derif und einer in derreturn). Ein Thread könnte also eine Nicht-Null sehenresource in demif bedingen, aber eine Nullreferenz (*) zurückgeben.

Meiner Ansicht nachUnsafeLazyInitialization.getInstance() kann null zurückgeben, auch wennResource ist unveränderlich. Ist es der Fall und warum (oder warum nicht)?

(*) Um meinen Standpunkt zur Neuordnung besser zu verstehen,dieser Blogbeitrag von Jeremy Manson, einer der Autoren des Kapitels 17 des JLS zum Thema Parallelität, erklärt, wie der Hashcode von String sicher über ein harmloses Datenrennen veröffentlicht wird und wie das Entfernen der Verwendung einer lokalen Variablen dazu führen kann, dass der Hashcode fälschlicherweise 0 zurückgibt zu einer möglichen Nachbestellung sehr ähnlich zu dem, was ich oben beschreibe:

Was ich hier getan habe, ist, einen zusätzlichen Lesevorgang hinzuzufügen: den zweiten Lesevorgang von Hash vor der Rückkehr. So seltsam es klingt und so unwahrscheinlich es auch sein mag, der erste Lesevorgang kann den korrekt berechneten Hashwert und der zweite Lesevorgang 0 zurückgeben! Dies ist im Speichermodell zulässig, da das Modell eine umfassende Neuordnung von Operationen ermöglicht. Der zweite Lesevorgang kann in Ihrem Code verschoben werden, sodass Ihr Prozessor dies vor dem ersten tut!

Antworten auf die Frage(10)

Ihre Antwort auf die Frage