Wie sollen wir die Thread-Sicherheit in Java gewährleisten?
Was ist threadsicher?
Ein Zustand, in dem die Synchronisation garantiert ist, wenn variable Daten zwischen Threads geteilt werden.
Was ist die Garantie für die Synchronisation?
Definiert als Erfüllung der beiden Punkte "Garantie der Atomizität" und "Garantie der Sichtbarkeit".
Was ist die Garantie für Atomizität?
Es ist möglich, externe Störungen vom Anfang bis zum Ende eines bestimmten Prozesses auszuschließen.
Was ist die Garantie für Sichtbarkeit?
Der neueste Wert kann für die Zeitreihe angegeben werden.
Insbesondere in Java bezieht sich die folgende Steuerungssyntax auf die bisher aufgetretenen Elemente.
synchronisierter Block
Synchronisieren Sie Operationen zwischen Blöcken, die dasselbe Objekt wie ein Argument angeben.
Die synchronisierte Qualifizierung für eine Methode entspricht einem synchronisierten Block für die eigene Instanz.
//Synchronisieren Sie Operationen zwischen den folgenden synchronisierten Blöcken
public void foo() {
synchronized (_SOME_OBJECT) {
// do something
}
}
public void bar() {
synchronized (_SOME_OBJECT) {
// do something
}
}
//Die folgenden sind äquivalent
public void foo() {
synchronized (this) {
// do something
}
}
public void synchronized foo() {
// do something
}
flüchtiger Modifikator
Gewährleisten Sie die Sichtbarkeit der angegebenen Variablen.
Es gibt einige Situationen, die für threadsichere Zwecke verwendet werden können, aber wir werden sie diesmal nicht behandeln.
Beispielsweise werden Variablen vom Typ int und vom Typ boolean durch Sprachspezifikationen garantiert atomar.
Betrachten Sie den Fall, in dem foo () und bar () für die folgende Implementierung mehrmals in unregelmäßigen Abständen von verschiedenen Threads aufgerufen werden.
public void foo() {
_object.update(); // _Verarbeitung zum Ändern des internen Status des Objekts
}
public void bar() {
if (_object.enabled()) {
_object.getData(); // enabled()Eine Ausnahme tritt auf, wenn aufgerufen wird, wenn false ist
}
}
Da bei der Verarbeitung von bar () keine Garantie für die Atomizität besteht, können die Methoden in der folgenden Reihenfolge aufgerufen werden. _object.enabled() -> _object.update() -> _object.getData()
public void foo() {
_object.update();
}
public void bar() {
if (_object.enabled()) {
_object.getData(); // _Der interne Zustand des Objekts ist nicht garantiert
}
}
Während der Verarbeitung im Block wird eine Synchronisationsgarantie hinzugefügt, dh die Atomizität wird ebenfalls garantiert. 【 _object.update() 】 -> 【 _object.enabled() -> _object.getData() 】 -> 【 _object.update() 】
Es ist leicht zu verstehen, ob Sie ein Bild haben, das in Blockeinheiten in der Verarbeitungswarteschlange gespeichert ist.
public void foo() {
synchronized (this) {
_object.update();
}
}
public void bar() {
synchronized (this) {
if (_object.enabled()) {
_object.getData(); // _Der interne Zustand des Objekts ist garantiert
}
}
}
Synchronisieren Sie Operationen zwischen Blöcken, die dasselbe Objekt wie ein Argument angeben
Der synchronisierte Block ist nur eine Garantie für die Synchronisation wie oben beschrieben. Die folgende Implementierung garantiert keine Synchronisation für die Verarbeitung in bar ().
public void foo() {
_object.update();
}
public void bar() {
synchronized (this) {
if (_object.enabled()) {
_object.getData(); // _Der interne Zustand des Objekts ist nicht garantiert
}
}
}
Eine Ergänzung für alle Fälle. Die Synchronisierung ist für Vorgänge zwischen Blöcken, die unterschiedliche Objekte als Argumente angeben, nicht garantiert.
public void foo() {
synchronized (_SOME_OBJECT) {
_object.update();
}
}
public void bar() {
synchronized (this) {
if (_object.enabled()) {
_object.getData(); // _Der interne Zustand des Objekts ist nicht garantiert
}
}
}
Um das Auftreten eines Deadlocks zu minimieren, ist es wünschenswert, die folgenden Prinzipien auf die Implementierung anzuwenden.
this
Nicht wünschenswert für Prinzipien, da es ein öffentliches Objekt für den Benutzer ist
Eine äquivalente synchronisierte Methode sollte ebenfalls nicht verwendet werden
privates Klassenfeld
Garantiert für Benutzer unzugänglich
Für Prinzipien nicht wünschenswert, da es sich um ein Objekt handelt, das von Instanzen gemeinsam genutzt wird
privates Instanzfeld
Garantiert für Benutzer unzugänglich
Und da es für jede Instanz generiert wird, ist es für das Prinzip optimal
In beiden Fällen darf das Sperrobjekt auch nicht null sein. Dies wird durch Hinzufügen des letzten Modifikators garantiert.
//Empfohlene Implementierung
class Shared {
private final Object _LOCK_OBJECT = new Object(); //Mach es nicht statisch
public void foo() {
synchronized (_LOCK_OBJECT) {
// do something
}
}
public void bar() {
synchronized (_LOCK_OBJECT) {
// do something
}
}
}
class User {
public void sample() {
final Shared object = new Shared();
new Thread(() -> object.foo()).start();
new Thread(() -> object.bar()).start();
}
}
//Implementierungen, die dies für Sperrobjekte verwenden
class Shared {
public void foo() {
synchronized (this) {
// do something
}
}
public void bar() {
synchronized (this) {
// do something
}
}
}
class User {
public void sample() {
final Shared object = new Shared();
//Diese Objektinstanz dient als Referenz auf das Sperrobjekt der gemeinsam genutzten internen Implementierung
//Daher verstößt es gegen das Prinzip "Minimierung des Freigabebereichs von Sperrobjekten".
}
}
In ähnlicher Weise sollte gemäß dem Prinzip der Minimierung des Freigabebereichs für jedes Ziel, für das die Thread-Sicherheit gewährleistet werden soll, ein Sperrobjekt definiert werden.
class Shared {
//Ziel anvisieren
private final Data _dataA = new Data();
private final Data _dataB = new Data();
private final Data _dateC = new Data();
private final Validator = _validator = new Validator();
//Objekt sperren
private final Object _DATA_A_LOCK = new Object();
private final Object _DATA_B_LOCK = new Object();
private final Object _DATA_C_LOCK = new Object();
public void foo() {
synchronized (_DATA_A_LOCK) {
_dataA.update();
}
synchronized (_DATA_B_LOCK) {
_dataB.update();
}
synchronized (_DATA_C_LOCK) {
_validator.execute();
_dataC.update();
}
}
public void bar() {
synchronized (_DATA_A_LOCK) {
_dataA.get();
}
synchronized (_DATA_B_LOCK) {
_dataB.get();
}
synchronized (_DATA_C_LOCK) {
_validator.execute();
_dataC.get();
}
}
}
Recommended Posts