Jedes Mal, wenn ich in einem nicht endgültigen Klassenfeld synchronisiere, wird eine Warnung angezeigt. Hier ist der Code:
public class X
{
private Object o;
public void setO(Object o)
{
this.o = o;
}
public void x()
{
synchronized (o) // synchronization on a non-final field
{
}
}
}
Deshalb habe ich die Codierung folgendermaßen geändert:
public class X
{
private final Object o;
public X()
{
o = new Object();
}
public void x()
{
synchronized (o)
{
}
}
}
Ich bin nicht sicher, ob der obige Code der richtige Weg ist, um in einem nicht endgültigen Klassenfeld zu synchronisieren. Wie kann ich ein nicht endgültiges Feld synchronisieren?
o
wird, auf das zum Zeitpunkt des Erreichens des synchronisierten Blocks verwiesen wurde. Wenn sich das Objekt, daso
sich auf Änderungen bezieht, ändert, kann ein anderer Thread den synchronisierten Codeblock ausführen.this
- ich würde empfehlen, eine endgültige Variable in der Klasse nur zum Zwecke des Sperren zu erstellen , wodurch verhindert wird, dass andere Personen dasselbe Objekt sperren.Das ist wirklich keine gute Idee, da Ihre synchronisierten Blöcke nicht mehr wirklich konsistent synchronisiert sind .
Angenommen, die synchronisierten Blöcke sollen sicherstellen, dass jeweils nur ein Thread auf gemeinsam genutzte Daten zugreift. Beachten Sie Folgendes:
Warum würden Sie wollen dies geschehen? Vielleicht gibt es einige sehr spezielle Situationen, in denen es sinnvoll ist ... aber Sie müssten mir einen bestimmten Anwendungsfall (zusammen mit Möglichkeiten zur Abschwächung des oben angegebenen Szenarios) vorstellen, bevor ich zufrieden bin es.
quelle
o
) - und während der Ausführung beginnt eine andere Liste zu mutieren. Wie wäre das eine gute Idee? Ich denke, wir sind uns grundsätzlich nicht einig darüber, ob es eine gute Idee ist, Objekte zu fixieren, die Sie auf andere Weise berühren. Ich wäre lieber in der Lage, über meinen Code nachzudenken, ohne zu wissen, was anderer Code in Bezug auf das Sperren tut.Ich stimme einem Kommentar von John zu: Sie müssen beim Zugriff auf eine nicht endgültige Variable immer einen Dummy für die endgültige Sperre verwenden, um Inkonsistenzen bei Referenzänderungen der Variablen zu vermeiden. Also auf jeden Fall und als erste Faustregel:
Regel 1: Wenn ein Feld nicht endgültig ist, verwenden Sie immer einen (privaten) Dummy für die endgültige Sperre.
Grund Nr. 1: Sie halten die Sperre und ändern die Referenz der Variablen selbst. Ein anderer Thread, der außerhalb der synchronisierten Sperre wartet, kann in den geschützten Block eintreten.
Grund Nr. 2: Sie halten die Sperre und ein anderer Thread ändert die Referenz der Variablen. Das Ergebnis ist das gleiche: Ein anderer Thread kann in den geschützten Block eintreten.
Bei Verwendung eines Dummy für die endgültige Sperre gibt es jedoch ein weiteres Problem : Möglicherweise werden falsche Daten angezeigt, da Ihr nicht endgültiges Objekt nur beim Aufruf von synchronize (Objekt) mit dem RAM synchronisiert wird. Als zweite Faustregel gilt:
Regel 2: Wenn Sie ein nicht endgültiges Objekt sperren, müssen Sie immer beides tun: Verwenden eines endgültigen Sperr-Dummys und der Sperre des nicht endgültigen Objekts für die RAM-Synchronisation. (Die einzige Alternative besteht darin, alle Felder des Objekts als flüchtig zu deklarieren!)
Diese Sperren werden auch als "verschachtelte Sperren" bezeichnet. Beachten Sie, dass Sie sie immer in derselben Reihenfolge aufrufen müssen, da Sie sonst ein Deadlock erhalten :
Wie Sie sehen, schreibe ich die beiden Schlösser direkt in dieselbe Zeile, weil sie immer zusammen gehören. Auf diese Weise können Sie sogar 10 Verschachtelungssperren erstellen:
Beachten Sie, dass dieser Code nicht beschädigt wird, wenn Sie nur eine innere Sperre wie
synchronized (LOCK3)
bei einem anderen Thread erwerben . Aber es wird kaputt gehen, wenn Sie einen anderen Thread wie diesen aufrufen:Es gibt nur eine Problemumgehung für solche verschachtelten Sperren beim Behandeln nicht endgültiger Felder:
Regel 2 - Alternative: Deklarieren Sie alle Felder des Objekts als flüchtig. (Ich werde hier nicht über die Nachteile sprechen, z. B. das Verhindern von Speicher in X-Level-Caches, auch für Lesevorgänge.)
Aioobe ist also ganz richtig: Verwenden Sie einfach java.util.concurrent. Oder beginnen Sie, alles über die Synchronisierung zu verstehen, und tun Sie dies selbst mit verschachtelten Sperren. ;)
Weitere Informationen dazu, warum die Synchronisierung bei nicht endgültigen Feldern unterbrochen wird, finden Sie in meinem Testfall: https://stackoverflow.com/a/21460055/2012947
Weitere Informationen dazu, warum Sie aufgrund von RAM und Caches überhaupt synchronisiert werden müssen, finden Sie hier: https://stackoverflow.com/a/21409975/2012947
quelle
o
mit einem synchronisierten (LOCK) umschließen müssen, um eine "Vorher-Geschehen" -Beziehung zwischen dem Setzen und dem Lesen des Objekts herzustelleno
. Ich diskutiere dies in einer ähnlichen Frage von mir: stackoverflow.com/questions/32852464/…Ich sehe hier nicht wirklich die richtige Antwort, das heißt, es ist vollkommen in Ordnung, dies zu tun.
Ich bin mir nicht mal sicher, warum es eine Warnung ist, daran ist nichts auszusetzen. Die JVM stellt sicher , dass Sie erhalten ein gültiges Objekt zurück (oder null) , wenn Sie einen Wert lesen, und Sie können auf synchronisieren jedes Objekt.
Wenn Sie vorhaben, die Sperre während der Verwendung tatsächlich zu ändern (anstatt sie beispielsweise von einer Init-Methode zu ändern, bevor Sie sie verwenden), müssen Sie die Variable festlegen, die Sie ändern möchten
volatile
. Dann alles , was Sie tun müssen, ist auf die Synchronisierung sowohl dem alten und dem neuen Objekt, und Sie können sicher den Wert ändern...
Dort. Es ist nicht kompliziert, das Schreiben von Code mit Sperren (Mutexen) ist eigentlich recht einfach. Das Schreiben von Code ohne sie (sperrfreier Code) ist schwierig.
quelle
EDIT: Diese Lösung (wie von Jon Skeet vorgeschlagen) hat möglicherweise ein Problem mit der Atomizität der Implementierung von "synchronized (object) {}", während sich die Objektreferenz ändert. Ich habe separat gefragt und laut Mr. Erickson ist es nicht threadsicher - siehe: Ist die Eingabe eines synchronisierten Blocks atomar? . Nehmen Sie es als Beispiel, wie man es NICHT macht - mit Links warum;)
Sehen Sie sich den Code an, wie es funktionieren würde, wenn synchronized () atomar wäre:
quelle
AtomicReference passt zu Ihrer Anforderung.
Von Java - Dokumentation über Atom- Paket:
Beispielcode:
Im obigen Beispiel ersetzen Sie durch
String
Ihr eigenesObject
Verwandte SE-Frage:
Wann wird AtomicReference in Java verwendet?
quelle
Wenn
o
sich während der Lebensdauer einer Instanz von nie ändertX
, ist die zweite Version besser, unabhängig davon, ob es sich um eine Synchronisierung handelt.Ob mit der ersten Version etwas nicht stimmt, kann nicht beantwortet werden, ohne zu wissen, was in dieser Klasse noch vor sich geht. Ich würde dem Compiler eher zustimmen, dass er fehleranfällig aussieht (ich werde nicht wiederholen, was die anderen gesagt haben).
quelle
Ich füge nur meine zwei Cent hinzu: Ich hatte diese Warnung, als ich eine Komponente verwendete, die durch den Designer instanziiert wurde, sodass das Feld nicht wirklich endgültig sein kann, da der Konstruktor keine Parameter annehmen kann. Mit anderen Worten, ich hatte ein quasi endgültiges Feld ohne das endgültige Schlüsselwort.
Ich denke, deshalb ist es nur eine Warnung: Sie machen wahrscheinlich etwas falsch, aber es könnte auch richtig sein.
quelle