Warum ist `synchronisiert (neues Objekt ()) {}` ein No-Op?
Im folgenden Code:
class A {
private int number;
public void a() {
number = 5;
}
public void b() {
while(number == 0) {
// ...
}
}
}
Wenn Methode b aufgerufen wird und dann ein neuer Thread gestartet wird, der Methode a auslöst, ist nicht garantiert, dass Methode b jemals die Änderung von @ siehnumber
und somitb
kann niemals enden.
atürlich könnten wir @ machnumber
volatile
, um dies zu beheben. Nehmen wir jedoch aus akademischen Gründen an, dassvolatile
ist keine Option:
Das JSR-133 FAQs sagt uns
Nach dem Verlassen eines synchronisierten Blocks geben wir den Monitor frei, der hat den Effekt, dass der Cache in den Hauptspeicher geleert wird, damit die von diesem Thread erstellten Schreibvorgänge für andere Threads sichtbar sind. Bevor wir einen synchronisierten Block eingeben können, erfassen wir den Monitor, der hat den Effekt, dass der Cache des lokalen Prozessors ungültig wird, damit die Variablen aus dem Hauptspeicher neu geladen werden.
Das klingt wie ich brauche nur beidea
undb
um ein beliebiges @ zu betreten und zu verlasssynchronized
-Block überhaupt, egal welchen Monitor sie benutzen. Genauer klingt es so ...:
class A {
private int number;
public void a() {
number = 5;
synchronized(new Object()) {}
}
public void b() {
while(number == 0) {
// ...
synchronized(new Object()) {}
}
}
}
... würde das Problem beseitigen und garantiert, dassb
wird die Änderung in @ seha
und damit wird auch irgendwann kündigen.
Die FAQs enthalten jedoch auch folgende Informationen:
Eine weitere Implikation ist, dass das folgende Muster, das einige Leute verwenden, um eine Speicherbarriere zu erzwingen, nicht funktioniert:
synchronized (new Object()) {}
Dies ist eigentlich ein No-Op, und Ihr Compiler kann es vollständig entfernen, da der Compiler weiß, dass kein anderer Thread auf demselben Monitor synchronisiert. Sie müssen eine Beziehung vor dem Eintreten einrichten, damit ein Thread die Ergebnisse eines anderen Threads anzeigt.
Now das ist verwirrend. Ich dachte, dass die synchronisierte Anweisung Caches zum Leeren bringt. Ein Cache kann sicherlich nicht so in den Hauptspeicher entleert werden, dass die Änderungen im Hauptspeicher nur von Threads gesehen werden können, die auf demselben Monitor synchronisiert werden, zumal für Volatile, die im Grunde dasselbe tun, wir nicht einmal einen benötigen überwachen, oder irre ich mich dort? Also, warum ist dies ein No-Op und verursacht nichtb
durch Garantie kündigen?