Heim  >  Artikel  >  Java  >  Beispielcode-Sharing zur Vermeidung von Deadlocks in Java

Beispielcode-Sharing zur Vermeidung von Deadlocks in Java

黄舟
黄舟Original
2017-06-18 09:48:231425Durchsuche

Deadlocks können in manchen Fällen vermieden werden. In diesem Artikel werden drei Techniken zur Vermeidung von Deadlocks demonstriert. Freunde, die sich für das Vermeiden von Deadlocks in Java interessieren, sollten aus diesem Artikel lernen

In manchen Fällen können Deadlocks vermieden werden. In diesem Artikel werden drei Techniken zur Vermeidung von Deadlocks demonstriert:

1 Sperrsequenz

3

Sperrreihenfolge

Wenn mehrere Threads dieselben Sperren erfordern, diese aber in unterschiedlicher Reihenfolge sperren, kann es leicht zu einem Deadlock kommen. Wenn Sie sicherstellen können, dass alle Threads Sperren in derselben Reihenfolge erhalten, tritt kein Deadlock auf. Schauen Sie sich das folgende Beispiel an:

Wenn ein Thread (z. B. Thread 3) einige Sperren benötigt, muss er die Sperren in einer bestimmten Reihenfolge erwerben. Es kann nachfolgende Sperren nur erwerben, nachdem es nacheinander die davor liegende Sperre erworben hat.


Zum Beispiel können Thread 2 und Thread 3 erst versuchen, Sperre C zu erwerben, nachdem sie Sperre A erworben haben (der Erwerb von Sperre A ist eine notwendige Bedingung für den Erwerb von Sperre C). Da Thread 1 bereits Sperre A besitzt, müssen Threads 2 und 3 warten, bis Sperre A freigegeben wird. Dann müssen sie A erfolgreich sperren, bevor sie versuchen, B oder C zu sperren.

Thread 1:
 lock A 
 lock B
Thread 2:
  wait for A
  lock C (when A locked)
Thread 3:
  wait for A
  wait for B
  wait for C
Das Sperren in der richtigen Reihenfolge ist ein wirksamer Mechanismus zur Verhinderung von Deadlocks. Allerdings erfordert diese Methode, dass Sie im Voraus alle möglichen Sperren kennen (und diese Sperren entsprechend anordnen), aber manchmal ist dies unvorhersehbar.

Sperrzeitlimit

Eine andere Möglichkeit, einen Deadlock zu vermeiden, besteht darin, beim Versuch, die Sperre zu erhalten, eine Zeitüberschreitung hinzuzufügen Wenn dieses Zeitlimit während des Versuchs, die Sperre zu erhalten, überschritten wird, gibt der Thread die Sperranforderung auf. Wenn ein Thread innerhalb eines bestimmten Zeitlimits nicht alle erforderlichen Sperren erfolgreich erhält, führt er einen Rollback durch und gibt alle erworbenen Sperren frei. Anschließend wartet er eine zufällige Zeitspanne, bevor er es erneut versucht. Diese zufällige Wartezeit gibt anderen Threads die Möglichkeit, zu versuchen, dieselben Sperren zu erhalten, und ermöglicht es der Anwendung, weiterzulaufen, ohne die Sperre zu erwerben (nach dem Sperrzeitlimit können Sie weiterlaufen und andere Dinge tun). Gehen Sie dann zurück und wiederholen Sie die vorherige Sperrlogik). Das Folgende ist ein Beispiel, das zwei Threads zeigt, die versuchen, dieselben zwei Sperren in unterschiedlicher Reihenfolge zu erhalten, einen Rollback durchzuführen und es erneut zu versuchen, nachdem eine Zeitüberschreitung aufgetreten ist:


Im obigen Beispiel versucht Thread 2 die Sperre 200 Millisekunden früher als Thread 1 erneut, sodass er zuerst zwei Sperren erfolgreich erwerben kann. Zu diesem Zeitpunkt versucht Thread 1, Sperre A zu erhalten und befindet sich im Wartezustand


. Wenn Thread 2 endet, kann Thread 1 diese beiden Sperren ebenfalls erfolgreich erwerben (es sei denn, Thread 2 oder andere Threads erwerben einige der Sperren, bevor Thread 1 die beiden Sperren erfolgreich erhält).

 Thread 1 locks A
Thread 2 locks B
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.

Es ist zu beachten, dass wir aufgrund des Sperrzeitlimits nicht davon ausgehen können, dass es sich bei diesem Szenario um einen Deadlock handeln muss. Es kann auch sein, dass der Thread, der die Sperre erhalten hat (was zu einer Zeitüberschreitung bei anderen Threads führt), lange braucht, um seine Aufgabe abzuschließen. Wenn außerdem viele Threads gleichzeitig um denselben Ressourcenstapel konkurrieren, versuchen diese Threads möglicherweise wiederholt, erhalten jedoch nie die Sperre, auch wenn ein Timeout- und Rollback-Mechanismus vorhanden ist. Wenn nur zwei Threads vorhanden sind und das Wiederholungszeitlimit zwischen 0 und 500 Millisekunden liegt, tritt dieses Phänomen möglicherweise nicht auf. Wenn jedoch 10 oder 20 Threads vorhanden sind, ist die Situation anders. Denn die Wahrscheinlichkeit, dass diese Threads auf gleiche Wiederholungszeiten warten, ist viel höher (oder so nahe, dass es zu Problemen kommt). (Der Timeout- und Wiederholungsmechanismus dient dazu, gleichzeitig auftretende Konkurrenz zu vermeiden. Wenn jedoch viele Threads vorhanden sind, besteht eine hohe Wahrscheinlichkeit, dass die Timeout-Zeit von zwei oder mehr Threads gleich oder nahe beieinander ist, sodass auch bei Konkurrenz tritt auf und es kommt zu einer Zeitüberschreitung. Später, da die Zeitüberschreitung gleich ist, beginnen sie gleichzeitig mit dem erneuten Versuch, was zu einer neuen Wettbewerbsrunde und neuen Problemen führt)

Es liegt ein Problem vor Mit diesem Mechanismus, der in Java nicht verwendet werden kann, legen Sie den Timeout-Zeitraum für synchronisierte synchronisierte Blöcke fest. Sie müssen eine benutzerdefinierte Sperre erstellen oder die Tools im Paket java.util.con

current

in Java 5 verwenden. Das Schreiben einer benutzerdefinierten Sperrklasse ist nicht kompliziert, würde aber den Rahmen dieses Artikels sprengen.

Deadlock-Erkennung

Die Deadlock-Erkennung ist ein besserer Deadlock-Verhinderungsmechanismus, der hauptsächlich für Fälle geeignet ist, in denen es unmöglich ist, sequentielle Szenarien zu erreichen Sperren und Sperrzeitüberschreitung sind nicht möglich. Immer wenn ein Thread eine Sperre erhält, wird dies in den thread- und sperrenbezogenen Datenstrukturen (Karte, Diagramm usw.) aufgezeichnet. Darüber hinaus muss jedes Mal, wenn ein Thread eine Sperre anfordert, diese auch in dieser Datenstruktur aufgezeichnet werden.

Wenn ein Thread keine Sperre anfordert, kann der Thread das Sperrdiagramm durchlaufen, um zu sehen, ob ein Deadlock auftritt. Beispielsweise fordert Thread A Sperre 7 an, aber Sperre 7 wird zu diesem Zeitpunkt von Thread B gehalten. Zu diesem Zeitpunkt kann Thread A prüfen, ob Thread B die Sperre angefordert hat, die derzeit von Thread A gehalten wird. Wenn Thread B eine solche Anforderung hat, ist ein Deadlock aufgetreten (Thread A besitzt Sperre 1 und fordert Sperre 7 an; Thread B besitzt Sperre 7 und fordert Sperre 1 an).

Natürlich ist ein Deadlock im Allgemeinen komplizierter als die Situation, in der zwei Threads die Sperren des anderen halten. Thread A wartet auf Thread B, Thread B wartet auf Thread C, Thread C wartet auf Thread D und Thread D wartet auf Thread A. Damit Thread A einen Deadlock erkennen kann, muss er nach und nach alle von B angeforderten Sperren erkennen. Ausgehend von der von Thread B angeforderten Sperre fand Thread A Thread C, dann Thread D und stellte fest, dass die von Thread D angeforderte Sperre von Thread A selbst gehalten wurde. Dies ist der Zeitpunkt, an dem erkannt wird, dass ein Deadlock aufgetreten ist.
Was sollen diese Threads also tun, wenn ein Deadlock erkannt wird?

Ein möglicher Ansatz besteht darin, alle Sperren aufzuheben, einen Rollback durchzuführen und eine zufällige Zeitspanne zu warten, bevor Sie es erneut versuchen. Dies ähnelt einem einfachen Sperrzeitlimit. Der Unterschied besteht darin, dass der Rollback nur dann erfolgt, wenn ein Deadlock aufgetreten ist, und nicht, weil die Sperranforderung abgelaufen ist. Obwohl es zu Backoffs und Wartezeiten kommt, kommt es dennoch wiederholt zu Deadlocks, wenn eine große Anzahl von Threads um denselben Sperrstapel konkurriert.

Eine bessere Lösung besteht darin, für diese Threads eine Priorität festzulegen, einen (oder mehrere) Threads zurückrollen zu lassen und die verbleibenden Threads so fortzusetzen, als ob kein Deadlock aufgetreten wäre. Sie benötigen Sperren. Wenn die diesen Threads zugewiesenen Prioritäten fest sind, haben dieselben Threads immer eine höhere Priorität. Um dieses Problem zu vermeiden, können beim Auftreten eines Deadlocks zufällige Prioritäten festgelegt werden.

Das obige ist der detaillierte Inhalt vonBeispielcode-Sharing zur Vermeidung von Deadlocks in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn