2 Stimmen

Synchronisierter Codeblock

Warum "synchronisieren" die Leute für nur 1 Codezeile? Was gibt es zu "synchronisieren"?

public final void addListener(Listener listener) {
  synchronized (listeners) {
    listeners.add(listener);
  }
}

EDIT: Ich danke Ihnen allen. Sehr gute Antworten von allen!

7voto

Michael Madsen Punkte 52883

synchronized bedeutet, dass, wenn mehrere Threads gleichzeitig versuchen, dieses Stück Code auszuführen, nur einer dieser Threads zu einem bestimmten Zeitpunkt innerhalb des Blocks erlaubt ist. synchronized (listeners) verwendet listeners als Sperrkennzeichen, was bedeutet, dass diese Einschränkung für alle Blöcke gilt, die sich auf diese Variable synchronisieren - wenn sich ein Thread in einem dieser Blöcke befindet, darf kein anderer Thread einen dieser Blöcke betreten.

Auch wenn es nur einen einzigen Funktionsaufruf in einem Block gibt, kann dies sinnvoll sein: Diese Funktion besteht aus vielen anderen Anweisungen, und die Kontrolle kann zu einem anderen Thread wechseln, während sich der erste inmitten dieser Funktion befindet. Wenn die Funktion nicht thread-safe Das kann zu Problemen führen, z. B. zum Überschreiben von Daten.

In diesem speziellen Fall besteht der Funktionsaufruf darin, einen Wert zu einer Sammlung hinzuzufügen listeners . Es ist zwar nicht unmöglich, eine thread-sichere Sammlung zu erstellen, aber die meisten Sammlungen sind nicht thread-sicher für mehrere Schreiber. Um also sicherzustellen, dass die Sammlung nicht durcheinander gerät, ist es notwendig, synchronized erforderlich ist.

EDIT: Um ein Beispiel dafür zu geben, wie die Dinge durcheinander geraten können, nehmen Sie diese vereinfachte Implementierung von add , wobei length ist die Anzahl der Elemente in der items Array:

public void Add(T item) {
  items[length++] = item;
}

Das length++ Bit ist nicht atomar; es besteht aus einem Lesen, einem Inkrement und einem Schreiben, und der Thread kann nach jedem dieser Schritte unterbrochen werden. Lassen Sie uns das also ein wenig umschreiben, um zu sehen, was wirklich passiert:

public void Add(T item) {
  int temp = length;
  length = length + 1;
  items[temp] = item;
}

Nehmen wir nun an, dass zwei Threads T1 und T2 gleichzeitig in Add eintreten. Hier ist ein möglicher Satz von Ereignissen:

T1: int temp = length;
T2: int temp = length;
T2: length = length + 1;
T2: items[temp] = item;
T1: length = length + 1;
T1: items[temp] = item;

Das Problem dabei ist, dass den gleichen Wert wird verwendet für temp von beiden Threads, so dass der letzte Thread, der den Thread verlässt, am Ende das Element überschreibt, das der erste dort abgelegt hat; Es gibt einen nicht zugewiesenen Posten ganz am Ende.

Es hilft auch nicht, wenn length stellt den nächsten zu verwendenden Index dar, so dass wir ein Preincrement verwenden können:

public void Add(T item) {
  items[++length] = item;
}

Wir schreiben das noch einmal um:

public void Add(T item) {
  length = length + 1;
  items[length] = item;
}

Dies ist eine mögliche Abfolge von Ereignissen:

T1: length = length + 1;
T2: length = length + 1;
T2: items[length] = item;
T1: items[length] = item;

Auch hier überschreibt der letzte Thread den ersten, aber jetzt ist das nicht zugewiesene Element das vorletzte Element.

3voto

paxdiablo Punkte 809679

Das liegt daran, dass "nur 1 Zeile Code" nichts dergleichen ist. Es mag eine Zeile Quellcode in Ihrer Datei sein, aber der tatsächliche Code, der hinter den Kulissen läuft, um dies zu erreichen, kann durchaus Hunderte von Anweisungen umfassen, tous die bei einem Aufgabenwechsel unterbrochen werden könnten.

Durch die Synchronisierung (hier und an anderen Stellen, die Sie verwenden möchten listeners in irgendeiner Weise), garantieren Sie, dass kein anderer Ausführungsstrang Ihnen den Boden unter den Füßen wegziehen kann, und umgekehrt.

1voto

ratchet freak Punkte 45968

Standardbeispiel:

count++;

wird dies hinter den Kulissen erweitert auf

int tmp=count;
tmp=tmp+1;
count=tmp;

(dies liegt daran, dass Prozessoren nicht direkt auf den Speicher zugreifen können und die Variablen in Register laden müssen)

dies hat Probleme, weil zwischen dem Laden count und das aktualisierte Ergebnis zu speichern, könnte ein anderer Thread es aktualisiert haben, was bedeutet, dass diese Aktualisierung verloren geht und zu fehlerhaftem Verhalten führt

1voto

XORshift Punkte 358

In dem von Ihnen angeführten Beispiel "synchronisieren" Sie nicht nur eine Codezeile, sondern sperren auch das Listener-Objekt und verhindern so, dass andere Threads, die ebenfalls auf dasselbe Objekt synchronisieren, darauf zugreifen können.

Angenommen, Sie hätten eine andere Methode in der Klasse, die addListener enthält:

public void removeListener(Listener listener) {
   synchronized (listeners) {
       listeners.remove(listener);
   }
}

Wenn Thread T das listeners-Objekt bei einem Aufruf von addListener gesperrt hat, müsste Thread S außerhalb des synchronisierten Blocks warten, bis Thread T die Sperre für das listeners-Objekt aufhebt. Dann würde er die Sperre übernehmen, den synchronisierten Block betreten und listeners.remove(listener) aufrufen.

Code, der direkt auf das Listener-Objekt zugreift, würde jedoch nicht warten, um die Sperre zu erhalten.

public void unsafeRemoveListener(Listener listener) {
   listeners.remove(listener);
}

0voto

James Drinkard Punkte 14572

Damit es nicht zu Konflikten kommt, wenn mehrere Threads durch eine Anwendung rasen, ist die offensichtliche Antwort. Auch bei Ajax oder Swing wollen Sie sicherstellen, dass der richtige Listener das richtige Objekt hat, auf das er hört.

Mit einigen der Ereignishandler-Toolkits, mit denen ich gearbeitet habe, abstrahieren sie Hörer zu Managern, so dass sie nicht dumme Dinge tun müssen, wie alle Hörer in eine arrayList zu setzen und dann Schleife durch, um die richtige Übereinstimmung zwischen dem Objekt und dem Hörer für sie zu finden.

Ich habe noch nicht mit Android gearbeitet, aber ich bin sicher, dass das Konzept ähnlich ist. Das falsche Objekt für den Hörer zu bekommen ist ein Problem.

HTH.

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