Verstehen, warum Deadlock in dieser Implementierung auftritt

8

Ich bin neu im Multithreading und bin auf dieses Beispiel gestoßen:

%Vor%

Dies verursacht die folgende Beispielausgabe:

%Vor%

d. h. es gibt einen Deadlock. Wenn wir jedoch die Reihenfolge der Sperren im zweiten Thread so ändern, dass sie jetzt so aussieht:

%Vor%

Es funktioniert wie erwartet, und eine Beispielausgabe sieht folgendermaßen aus:

%Vor%

Kann mir jemand erklären, was in der ersten Sache passiert, die den Deadlock verursacht, und warum repariert die Änderung im zweiten Code das?

    
SexyBeast 25.04.2016, 06:47
quelle

3 Antworten

5

Hier ist ein mögliches Szenario für den ersten Fall:

Thread 1 erwirbt Lock1 und geht für 10 Millisekunden in den Ruhezustand. Jetzt erwirbt Thread 2 Lock2 und geht für 10 Millisekunden in den Ruhezustand.

Jetzt versucht Thread 1, Lock2 zu erwerben, kann es aber nicht bekommen, da es von Thread 2 erworben wurde, während Thread 2 versucht, Lock1 zu erhalten, was durch Thread 1 gesperrt ist.

Im zweiten Fall nehmen wir an, dass der erste Thread zur Ausführung ausgewählt wurde. Er ruft Lock1 auf, während der andere Thread blockiert wird, weil er versucht, auch Lock1 zu erhalten. Jetzt wird Thread 1 in den Ruhezustand versetzt und mit dem zweiten (verschachtelten) synchronisierten Block fortgefahren. Es versucht es zu bekommen (weil es noch frei ist) - jetzt hat er 2 Schlösser. Der andere Thread ist immer noch blockiert.

Erst nachdem die Ausführung beendet ist, gibt es beide -Sperren frei (da es den synchronisierten Block verlässt), und jetzt kann die JVM frei entscheiden, welcher Thread ausgewählt werden soll, aber es spielt keine Rolle, die gleiche Logik passiert.

    
Maroun 25.04.2016, 06:56
quelle
2

Was Sie hier sehen, ist die Sperranordnung, eine gängige Methode zur Vermeidung von Deadlocks.

Betrachten Sie im ersten Fall die folgende Reihenfolge der Ausführung mit den aktuellen Befehlszeigern, die sich an der markierten Stelle für jeden Thread befinden:

%Vor%

Nun versucht Thread 1, als nächstes Lock 2 zu erhalten, kann aber nicht wie Lock 2 von Thread 2 gehalten werden. Und Thread 2 versucht, als nächstes Lock 1 zu erhalten, kann es aber nicht, weil es von Thread 1 gehalten wird zirkuläre Ressourcenabhängigkeit und führt daher zu Deadlock.

Eine globale Methode, um dies zu verhindern, besteht darin, sicherzustellen, dass alle Sperren eine Gesamt Reihenfolge haben und Sperren immer in dieser Gesamtreihenfolge erfasst werden.

Der Beweis, dass das funktioniert, ist trivial: Da alle Sperrabhängigkeiten in der gesamten Reihenfolge "nach unten" sind, können Sie keine Sperrzyklen haben.

    
dhke 25.04.2016 07:17
quelle
1

Nun, in deinem ersten Codeabschnitt hält der erste Thread Lock1 und der zweite Thread Lock2 offensichtlich. Dann versuchen sie, sich gegenseitig die Sperren zu nehmen, aber sie scheitern, weil die anderen Sperren bereits von dem anderen Thread gehalten werden, so dass ein Deadlock erstellt wird.

Im zweiten Teil des Codes hält der zweite Thread jedoch Lock2 nicht und blockiert alles, bis der erste Thread die erste Sperre freigibt. Der erste Thread wird die erste Sperre freigeben, nachdem er auch die zweite greift, wie aus der Ausgabe ersichtlich.

    
Idos 25.04.2016 06:54
quelle