7

Nehmen wir an, ich habe die folgende Klasse, die schwer gelesen werden, aber nur gelegentlich geschrieben. Es wird in einem Multi-Threaded-Web-App verwendet werden, so dass es Thread-sicher sein muss:Synchronisieren Schreibzugriff auf flüchtige Feld (Günstige Lese-Schreib-Block)

public class Foo { 
    private volatile String foo; 
    public String getFoo() { 
     return foo; 
    } 
    public synchronized String setFoo(String in) { 
     this.foo = in; 
    } 
} 

Java Concurrency (http://www.ibm.com/developerworks/java/library/j-jtp06197/index.html) heißt es, dass dies eine fragile Weise ist Schreibzugriff zu schützen, während des Lesezugriff zu verbessern. Was ist eine stärkere Alternative zu diesem Muster? Oder irgendeine Alternative, wenn foo in einer lese-schweren Umgebung veränderbar sein soll? Vielen Dank.

Antwort

14

Volatile bietet schnelle gewinde sichere Sperre freie Zugang zu einem Gebiet ohne Synchronisation

private volatile String foo; 

public String getFoo() { 
    return foo; 
} 
public void setFoo(String in) { 
    this.foo = in; 
} 

flüchtigen Probleme lösen 3 1) -Speicher Sichtbarkeits 2) atomic schreibt für Doppel- und lange Felder 3) verbieten Anweisungen Neuordnen. Aber es ist nicht genug, wenn Sie mehrere Operationen über ein Feld als eine atomare Transaktion benötigen, wie zum Beispiel Inkrement. Dieser Code wird

gebrochen
private volatile int id; 

public void incrementId() { 
    id++; 
} 

denn wenn zwei Threads es unerwuenscht das Ergebnis ablesen und erhöhen und speichern dann wird das Ergebnis des ersten Schrittweite, mit dem Ergebnis des zweiten Schrittes überschreibt. Um dies zu verhindern müssen wir Synchronisation

private int id; 

public synchronized int nextId() { 
     return ++id; 
} 

oder java.util.concurrent.atomic Paket

private AtomicInteger id = new AtomicInteger(); 

public void incrementId() { 
    return id.incrementAndGet(); 
} 
+1

Das Markieren einer Variablen als flüchtig macht es nicht threadsicher. Wenn das ganze OP das Lesen/Schreiben macht, dann ist es threadsicher (dies wäre nicht wahr, wenn mehrere Threads die Unterstützungsvariable inkrementieren würden ...). –

+0

Also gibt es eine Situation, in der sowohl volatile als auch synchronisierte angemessen wären? – oberger

+2

Ich bin sicher, dass es keine solche Situation gibt. Wenn Sie den Zugriff auf ein Feld synchronisieren, ist volatile redundant. –

2

verwenden Wenn alles, was Sie tun foo Einstellung, dann brauchen Sie nicht zu synchronisieren die Methode. die Angabe der Volatilität ist ausreichend.

+0

Und wenn ich mehr in der Setter-Methode mache? Wie zum Beispiel Kopieroperation oder Teilstringoperation. Würde ich nur synchronisieren oder beide verwenden? – oberger

+0

Jede andere Aktion in der Setter, die Atomizität in Bezug auf den Wert der Zeichenfolge benötigt, erfordert die Verwendung der Synchronisation, die den flüchtigen Modifikator nutzlos macht –

+1

@OwenBerger - wenn Sie mehr Arbeit im Setter tun müssen, dann ja, würden Sie müssen synchronisiert werden. Volatile ist in diesem Szenario jedoch immer noch nützlich, da es Ihnen ermöglicht, den Getter nicht zu synchronisieren (vorausgesetzt, es ist Ihnen egal, wenn ein Aufrufer an den Getter einen alten Wert erhält, während ein anderer Aufrufer im Setter ist). – jtahlborn

2

Am link gesagt, Sie gibt es diesen Code für „selten Updates“ Nutzung:

@ThreadSafe 
public class CheesyCounter { 
    // Employs the cheap read-write lock trick 
    // All mutative operations MUST be done with the 'this' lock held 
    @GuardedBy("this") private volatile int value; 

    public int getValue() { return value; } 

    public synchronized int increment() { 
     return value++; 
    } 
} 

Die increment Methode wird nur synchronisiert verwenden, weil es mehr zu tun als nur den Wert von value Einstellung wie in der angegebenen Beschreibung, wenn alles, was Sie tun, ist this.foo = in; das ist atomar. Im Text bedeutet die "Zerbrechlichkeit dieses Musters", dass Dinge sehr schnell unordentlich werden können, wenn Sie flüchtige und andere Synchronisationsmethoden mischen, um mehr als nur einfache Beispiele zu machen. Siehe Paket java.util.concurrent.locks für die Schnittstellen Bedingung und Sperre und die Klasse ReentrantLock. Ich denke das, und das Verwenden von synchronisiert ist, was der Autor durch "stärkere Alternativen" meint. Sie sollten auch sehen Object.wait, Object.notify und Object.notifyAll wenn Sie das noch nicht wissen.