PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Verständnisproblem mit Java synchronized


mittelding
2012-03-24, 22:44:10
Hallo!

Ich tu mich zugegebenermaßen etwas schwer mit dem synchronized Keyword in Java zur Threadsynchronisierung. Mir ist schon klar warum man Synchronisierung braucht, aber die Auswirkungen von synchronized kann ich noch nicht ganz überschauen. Aber mal von vorne.

Da man ja Methoden und Blöcke mit synchronized markieren kann, soll es zunächst um die Variante für Methoden gehen.

Wenn man also eine Methode auf synchronized setzt, dann kann sie nur von einem Thread am Stück ausgeführt werden, ohne dass ein anderer Thread sie zwischenzeitlich nochmal ausführen kann. Schön und gut.

Aber:

Die (objektspezifischen) Variablen/Objekte, die man innerhalb dieser Methode manipuliert, die sind in dieser Zeit trotzdem nicht vor Änderungen von außen geschützt, richtig? Wenn ich also noch eine zweite (non-synchronized) Methode habe, die könnte zwischenzeitlich trotzdem reinpfuschen und meine angestrebte Konsistenz kaputt machen?

Bsp:


public void synchronized example (int number) {
this.a = number;
this.b = number;

Print("an exakt dieser Stelle sollten a und b identisch sein, vielleicht aber auch nicht");
}


Hier hätte ich zwar die Sicherheit, dass diese Methode nicht von mehreren Threads gleichzeitig ausgeführt wird und sich deswegen a und b am Ende unterscheiden. Aber ich hätte nicht die Sicherheit, dass a während dem setzen von b nicht bereits wieder von einem anderen Thread in einer anderen (nicht-synchronisierten) Methode geändert wurde, also wären a und b am ende evt. trotzdem unterschiedlich. Liege ich da richtig?

----

Zweite Frage, etwas kürzer: angenommen, eine Klasse hat 10 synchronized Methoden. Wird eine davon von Thread X ausgeführt, so kann eben diese von Thread Y nicht gleichzeitig ausgeführt werden. Könnte aber eine der anderen 9 Methoden in dieser Zeit ausgeführt werden von Thread Y? Oder anders: sobald eine synchronized-Methode läuft, ist dann nur diese für andere Threads gesperrt oder gleich auch alle anderen synchronized-Methoden, wobwohl diese garnicht betroffen sind?

Ich vermute stark letzteres, wollte nur nochmal sicher gehen.

---

Jetzt noch zu der Variante, bei welcher Blöcke mit synchronized markiert werden. Hier kann man ja hinter dem Keyword in runden Klammern ein Objekt angeben, dessen Monitor benutzt werden soll. Da dies bei Methoden nicht geht, sind synchronized Methoden implizit äquivalent zu

synchronized(this) { // Hier erst der Methodencode }

Ich stelle mir das so vor, dass nun der Monitor des Objekts überwacht, dass die Methode nur einmal ausgeführt wird. Was aber, wenn man dort nicht this angibt, sondern eine Objektvariable?

synchronized(this.a) { // do sth. mit a }

Was ist hier der Sinn?



Vielen Dank

Ectoplasma
2012-03-25, 03:12:18
public void synchronized example (int number) {
this.a = number;
this.b = number;

Print("an exakt dieser Stelle sollten a und b identisch sein, vielleicht aber auch nicht");
}


Hier hätte ich zwar die Sicherheit, dass diese Methode nicht von mehreren Threads gleichzeitig ausgeführt wird und sich deswegen a und b am Ende unterscheiden. Aber ich hätte nicht die Sicherheit, dass a während dem setzen von b nicht bereits wieder von einem anderen Thread in einer anderen (nicht-synchronisierten) Methode geändert wurde, also wären a und b am ende evt. trotzdem unterschiedlich. Liege ich da richtig?


Wenn du etwas nicht synchronisierst, dann kann natürlich alles mögliche passieren. Die Antwort auf deine Frage ist jedenfalls ja, du liegst richtig.


Zweite Frage, etwas kürzer: angenommen, eine Klasse hat 10 synchronized Methoden. Wird eine davon von Thread X ausgeführt, so kann eben diese von Thread Y nicht gleichzeitig ausgeführt werden. Könnte aber eine der anderen 9 Methoden in dieser Zeit ausgeführt werden von Thread Y?


Nein, in der Regel synchronisierst du in einer modernen Sprache keinen Code-Abschnitt, sondern du synchronisierst dich auf ein Objekt. Hier ist die von dir bereits genannte Erstazschreibweise.


public void example (int number) {
synchronized (this) {
this.a = number;
this.b = number;
}

Print("an exakt dieser Stelle sollten a und b identisch sein, vielleicht aber auch nicht");
}


Das heißt, dass das die Synchronisation impliziet auf einer Objektrefrenz statt findet. Alle Zugriffe auf "this", werden hiermit synchronisiert. Also sind auch automatisch alle anderen Methoden synchronisiert, die "this" als Monitor verwenden.


Was aber, wenn man dort nicht this angibt, sondern eine Objektvariable?


Das kannst du machen. Damit kann man genau bestimmen, welche Attribute eines Objektes synchronisiert werden sollen und welche nicht.

Übriegens läßt sich, wie auch in fast in keiner anderen Sprache, ein Code-Abschnitt global synchronisieren. Dazu müsste man die Interrupts abschalten (ala CLI Befehl). In Java könnte man es auch mit dem Class.class Monitor probieren. Allerdings wäre das von der Performance her fatal.

Shink
2012-03-25, 08:27:02
Wenn ich also noch eine zweite (non-synchronized) Methode habe, die könnte zwischenzeitlich trotzdem reinpfuschen und meine angestrebte Konsistenz kaputt machen?
Ja


sobald eine synchronized-Methode läuft, ist dann nur diese für andere Threads gesperrt oder gleich auch alle anderen synchronized-Methoden, wobwohl diese garnicht betroffen sind?
Es muss alles warten, was auf this "synchronized". Also z.B. auch ein synchronized(this) Block.

Jetzt noch zu der Variante, bei welcher Blöcke mit synchronized markiert werden. Hier kann man ja hinter dem Keyword in runden Klammern ein Objekt angeben, dessen Monitor benutzt werden soll. Da dies bei Methoden nicht geht, sind synchronized Methoden implizit äquivalent zu

synchronized(this) { // Hier erst der Methodencode }

Ich stelle mir das so vor, dass nun der Monitor des Objekts überwacht, dass die Methode nur einmal ausgeführt wird. Was aber, wenn man dort nicht this angibt, sondern eine Objektvariable?
Erstens ist es sehr sinnvoll, Blöcke zu synchronisieren statt Methoden: Je kleiner der zu synchronisierende Block, desto weniger Performance-Einbußen. Außerdem sind Methoden ja vielleicht Teil der API und da sollte man auf so etwas keine Rücksicht nehmen müssen.

Zweitens kann ich, wie du schon schreibst, nur auf ein gewisses Attribut sperren. Oft verwendet man sogar ein Dummy-Attribut, das keinen anderen Zweck hat - es könnte ja jemand unser Attribut auf null setzen, das wäre natürlich fatal. Ein final field, initialisiert mit new Object(), tut es dafür.

Drittens könnte man sich auch das Attribut, auf dessen Zugriff gewartet werden muss, mitgeben lassen - es muss ja nicht unbedingt sein, dass unser gewünschtes Sperrverhalten etwas mit this zu tun hat.