2016-03-25 9 views
0

Von Java Concurrency in der Praxis:Unterschied in Speichermodell Semantik zwischen Endverbrauch und flüchtigen Feldern in Java

Wenn ein Feld volatile deklariert wird, werden die Compiler und Runtime auf in Kenntnis gesetzt, dass dieser Variable gemeinsam genutzt wird und dass Operationen auf es sollte nicht mit anderen Speicheroperationen neu angeordnet werden. Flüchtige Variablen sind nicht in Registern oder in Caches zwischengespeichert, wo sie von anderen Prozessoren versteckt sind, so dass ein Lesen einer flüchtigen Variablen immer die meisten jüngsten Schreiben von einem beliebigen Thread zurückgibt. (p25)

Und

Schluss Felder können nicht geändert werden (obwohl die Objekte, die sie beziehen sich auf können geändert werden, wenn sie wandelbar sind), aber sie haben auch spezielle Semantik unter dem Java-Speichermodell. Es ist die Verwendung von Endfeldern, die die Garantie der Initialisierungssicherheit ermöglicht (siehe Abschnitt 3.5.2) , die unverrückbare Objekte ohne Synchronisation frei zugänglich machen und freigeben kann. (p32)

eine unsichere Veröffentlichung Rezitieren:

public class Holder { 
     private int n; 
     public Holder(int n) { this.n = n; } 
     public void assertSanity() { 
     if (n != n) // might be true for other threads. 
    } 
} 

Der Wert von n überraschenderweise könnte abgestanden werden durch andere Threads gesehen. Aber final Modifier würde den Trick tun. Ähnlich wie volatile, oder? Sind final Felder intrinsisch volatile? (eine mögliche Erklärung, warum final volatile ist nicht erlaubt)

Antwort

1

Ist es ein möglicher Grund private volatile ist nicht erlaubt?

private volatile ist zulässig.

Meinst Du final volatile? Ja, diese Modifikatoren sind unvereinbar ihrer Natur nach - final var, die Wert/Referenz kann nicht geändert werden, brauchen keine zusätzlichen volatile Goodies (und die damit verbundenen Overhead), da die Mutation von final Feld unmöglich ist, und liest über mehrere Threads konsistent sind

Aber JMM bietet Initialisierung Volatile-Stil Konsistenz für final Felder. AFAIK wurde in JSR 133 (enthalten in Java SE 5.0) implementiert. Vor diesem JSR init liest während der Daten Rennens unvereinbar sein könnte (und null oder einen Zwischenwert zum Beispiel Rück)

PS: Ich habe classic article gefunden, die Ihr Problem erwähnt. Empfehlen Sie es (und den zweiten Teil)

+0

Danke. Die Aussage wurde korrigiert. Die Modifikation ist in Ordnung. Aber bedenken Sie das mitgelieferte Beispiel, final macht auch einige volatile wie Magie sogar während der Initialisierung, so dass es keine veralteten Lesevorgänge gibt, liefert JMM eine ähnliche Semantik für beide? – abksrv

+0

sehe meine feste Antwort. Es ist besser, einige zusätzliche Informationen dort zu platzieren, nicht in Kommentaren :) – Cootri

0

volatile hat nur Relevanz für Änderungen der Variablen selbst, nicht das Objekt, auf das es sich bezieht. Es macht keinen Sinn, ein final volatile Feld zu haben, weil final Felder nicht geändert werden können. Erklären Sie einfach das Feld final und es sollte in Ordnung sein.

2

Nein, final Felder sind nicht an sich volatile.

Wenn sie wären, wäre das unnötig teuer gewesen, denn in den meisten Fällen müssen Sie eine StoreLoad-Sperre nach einem volatile schreiben.

Dies kann für final Felder vermieden werden, da Sie eine zusätzliche Einschränkung haben, die Ihnen helfen kann - Sie wissen, dass final Felder müssen initialisiert werden, wenn die entsprechende Klasse oder Instanz Objekt vollständig initialisiert wird.

Die Spezifikation kann etwas schwer zu lesen sein (werfen Sie einen Blick auf section 17.5 der JLS), aber bedenken Sie, dass, wie der berüchtigte JMM Causality-Abschnitt, der Hauptpunkt war, formal zu beschreiben, was das intuitive Verhalten wäre die meisten Leute.

Wie für die Umsetzung, es erfordert in der Regel 2 Dinge:

  1. Gewährleistung, dass final Feld speichert, einschließlich der Geschäfte in der Kette, wenn das Feld eine Referenz ist, kann nicht mit Geschäften außerhalb des Konstrukteurs erst nachbestellt werden. Dies ist oft ein No-Op, auch wenn Sie Ihren Konstruktor inline, wenn die zugrunde liegende Hardware-Architektur ein starkes Speichermodell (wie x86) hat.

  2. Sicherstellen, dass das erste Feld final in einem bestimmten Thread nicht mit der ersten Ladung in demselben Thread der entsprechenden Referenz, zu der das Feld gehört, neu angeordnet werden kann. Dies ist fast immer ein No-Op, da alle Compiler und die meisten Hardwarearchitekturen Lastabhängigkeiten berücksichtigen.

Am Ende die viel weniger teuer auf den meist Architekturen LoadStor und StoreStor Barrieren sollten für die Umsetzung final Felder ausreichen.

===

Sie mehr darüber lesen können, wie final Felder sollten in unter die Decke realisiert werden:

===

P. S. Eine unsichere Veröffentlichung ist sogar in Gegenwart von final Feldern gefährlich. Für einige Vorbehalte siehe here.