23 Stimmen

Können Sie sicher auf einem Java-Methodenparameter synchronisieren?

Nehmen Sie diesen Code:

public class MyClass {
    private final Object _lock = new Object();
    private final MyMutableClass _mutableObject = new MyMutableClass()

    public void myMethod() {
        synchronized(_lock) { // wir synchronisieren auf der Instanzvariable _lock
            // mach etwas mit mutableVar 
            // (d.h. rufe eine "set"-Methode auf _mutableObject auf)
        }
    }
}

Stellen Sie sich nun vor, dass der Code in myMethod() an eine Hilfsklasse delegiert wird, der Sie das Schloss übergeben:

public class HelperClass {
    public helperMethod(Object lockVar, MyMutableClass mutableVar) {
        synchronized(lockVar) { // wir synchronisieren jetzt auf einem Methodenparameter, 
                                // jeder Thread hat seine eigene Kopie
            // mach etwas mit mutableVar 
            // (d.h. rufe eine "set"-Methode auf mutableVar auf)
        }
    }
}

Kann "myMethod" umgeschrieben werden, um die HelperClass zu verwenden, indem Sie ihr Schloss übergeben, damit alles weiterhin thread-sicher ist? d.h.,

public void myMethod() {
    _helperObject.helperMethod(_lock, _mutableObject);
}

Ich bin mir nicht sicher, weil Java das lockVar per Wert übergibt und jeder Thread eine separate Kopie von lockVar erhält (obwohl jede Kopie auf dasselbe Objekt im Heap zeigt). Ich denke, die Frage kommt darauf an, wie das 'synchronized'-Schlüsselwort funktioniert - sperrt es die Variable oder den Wert im Heap, auf den die Variable verweist?

15voto

Die Synchronisation erfolgt an Objekten, nicht an Variablen.

Variablen/Mitglieder enthalten manchmal Objekte und es ist das resultierende Objekt, das in der Variable x enthalten ist, auf das tatsächlich mit synchronized(x) synchronisiert wird.

Es gibt noch ein paar andere Probleme mit der Thread-Sichtbarkeit von Variablen (z.B. könnte ein "veraltetes" Objekt aus einer Variable gelesen werden), aber das trifft hier nicht zu: Es findet keine Neuweisung von _lock statt und die Sichtbarkeit der anfänglichen ("finalen") Zuweisung ist garantiert. Daher ist es garantiert, dass in diesem Fall der Methodenparameter immer das richtige (gleiche) Objekt enthält, das für die Synchronisation verwendet wird.

Wenn sich jedoch das verwendete Sperr-Objekt ändert (wobei vermutlich _lock nicht final ist), dann müssten die entsprechenden Werte/Thread-Sichtbarkeit neu bewertet werden, aber sonst unterscheidet es sich nicht von einem beliebigen Zugriff zwischen Threads.

Frohes Programmieren.

0 Stimmen

Tatsächlich war mein Beispiel nicht wirklich korrekt. Ich hatte nicht vor, _mutableObject als final zu markieren. Ich glaube nicht, dass dieses Sperren funktionieren wird WENN _mutableObject im übergeordneten Objekt nicht final ist. Wenn Sie diesen Wert als Variable in die Hilfsmethode geben und dann _mutableObject in der übergeordneten Klasse so ändern, dass es auf etwas anderes zeigt, hält die Hilfsmethode, wenn sie ausgeführt wird, nicht mehr auf den korrekten Verweis für _mutableObject. Das Schloss wird immer noch geschlossen sein, aber Sie werden einen veralteten Wert von _mutableObject "schützen". Das war eigentlich das, was meine Frage aufzeigen sollte.

0 Stimmen

Ich werde Ihre Antwort akzeptieren und davon ausgehen, dass dies nur funktioniert, wenn _mutableObject in der übergeordneten Klasse 'final' ist. Das _lock-Objekt muss immer als final deklariert werden, unabhängig davon, welche Version meines Beispiels Sie verwenden. Tatsächlich hätte ich mein Beispiel nicht mit einem "veränderbaren" Objekt erstellen sollen. Ich hätte einfach beschrieben, dass ich eine nicht-finale Variable schützen wollte, nennen wir es "private Object _object". Ich möchte, dass das Schloss verhindert, dass sich dieser Wert ändert. Wenn das Beispiel so geändert wird, denke ich nicht, dass Sie das Sperren an eine Hilfsmethode übergeben können, da die Hilfsmethode auf eine veraltete _object-Referenz zugreift.

0 Stimmen

@Android Dev Ein für eine Sperrung Objekt verwendeter Gegenstand sollte stabil und eindeutig für jede gegenseitig ausschließende Synchronisierung sein. Die durch die Synchronisierung geschützte Variable (oder das Objekt) kann sich ändern (der Synchronisierungskontext wird die Speicherbarrieren und Garantien für das Vorher-Ereignis sicherstellen, solange all diese Synchronisierungen auf demselben Sperrung Objekt erfolgen). Es ist im Allgemeinen keine gute Idee, ein für eine Sperre verwendetes Objekt auszutauschen (das Sperrung Objekt kann verändert werden und das wird die Verwendung mit synchronized nicht beeinträchtigen - die Bedeutung ist, dass dasselbe Objekt für die Synchronisierung verwendet wird).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X