Diese Fragen wurden mir kürzlich in einem Interview gestellt.
Ich antwortete, dass ein Deadlock auftritt, wenn das Interleaving schief geht, aber der Interviewer bestand darauf, dass ein Programm geschrieben werden kann, das unabhängig vom Interleaving immer in einen Deadlock gerät.
Können wir ein solches Programm schreiben? Können Sie mich auf ein solches Beispielprogramm verweisen?
java
concurrency
deadlock
user2434
quelle
quelle
Antworten:
UPDATE: Diese Frage war das Thema meines Blogs im Januar 2013 . Danke für die tolle Frage!
Hier ist ein Beispiel in C #. Beachten Sie, dass das Programm anscheinend keine Sperren und keine gemeinsam genutzten Daten enthält. Es hat nur eine einzige lokale Variable und drei Anweisungen und blockiert dennoch mit 100% iger Sicherheit. Es würde schwer fallen, ein einfacheres Programm zu entwickeln, das mit Sicherheit blockiert.
Übung für den Leser Nr. 1: Erklären Sie, wie dies blockiert. (Eine Antwort finden Sie in den Kommentaren.)
Übung für den Leser Nr. 2: Demonstrieren Sie den gleichen Deadlock in Java. (Eine Antwort finden Sie hier: https://stackoverflow.com/a/9286697/88656 )
quelle
Die Verriegelung hier stellt sicher, dass beide Verriegelungen gehalten werden, wenn jeder Thread versucht, den anderen zu verriegeln:
Interessant, jconsole auszuführen, wodurch der Deadlock auf der Registerkarte "Threads" korrekt angezeigt wird.
quelle
sleep
einen richtigen Riegel ersetzen : Theoretisch haben wir hier eine Rennbedingung. Obwohl wir fast sicher sein können, dass 0,5 Sekunden ausreichen, ist es für eine Interviewaufgabe nicht allzu gut.Ein Deadlock tritt auf, wenn Threads (oder wie auch immer Ihre Plattform ihre Ausführungseinheiten nennt) Ressourcen erwerben, wobei jede Ressource jeweils nur von einem Thread gehalten werden kann und diese Ressourcen so hält, dass die Holds nicht vorweggenommen werden können Zwischen den Threads besteht eine "kreisförmige" Beziehung, sodass jeder Thread im Deadlock darauf wartet, eine Ressource zu erhalten, die von einem anderen Thread gehalten wird.
Eine einfache Möglichkeit, einen Deadlock zu vermeiden, besteht darin, den Ressourcen eine vollständige Reihenfolge zu geben und die Regel festzulegen, dass Ressourcen immer nur von Threads in der richtigen Reihenfolge erfasst werden . Umgekehrt kann ein Deadlock absichtlich erstellt werden, indem Threads ausgeführt werden, die Ressourcen erfassen, diese jedoch nicht in der richtigen Reihenfolge abrufen. Beispielsweise:
Zwei Fäden, zwei Schlösser. Der erste Thread führt eine Schleife aus, die versucht, die Sperren in einer bestimmten Reihenfolge abzurufen, der zweite Thread führt eine Schleife aus, die versucht, die Sperren in der entgegengesetzten Reihenfolge abzurufen. Jeder Thread gibt beide Sperren frei, nachdem die Sperren erfolgreich erworben wurden.
Nun gab es einige Kommentare in dieser Frage, die auf den Unterschied zwischen der Wahrscheinlichkeit und der Gewissheit eines Deadlocks hinweisen . In gewissem Sinne ist die Unterscheidung ein akademisches Problem. Aus praktischer Sicht würde ich mir sicherlich ein laufendes System wünschen, das nicht mit dem oben geschriebenen Code blockiert ist :)
Interviewfragen können jedoch manchmal akademisch sein, und diese SO-Frage enthält das Wort "sicher" im Titel. Was folgt, ist also ein Programm, das mit Sicherheit ins Stocken gerät. Es
Locker
werden zwei Objekte erstellt, von denen jedes zwei Sperren erhält und eineCountDownLatch
zum Synchronisieren zwischen den Threads verwendet wird. JedesLocker
Schloss sperrt das erste Schloss und zählt dann den Riegel einmal herunter. Wenn beide Fäden eine Verriegelung erhalten und die Verriegelung heruntergezählt haben, gehen sie an der Verriegelungsbarriere vorbei und versuchen, eine zweite Verriegelung zu erlangen, aber in jedem Fall hält der andere Faden bereits die gewünschte Verriegelung. Diese Situation führt zu einem gewissen Deadlock.quelle
Hier ist ein Java-Beispiel, indem Sie Eric Lipperts folgen:
quelle
Hier ist ein Beispiel aus der Dokumentation:
quelle
Object invokeAndWait(Callable task)
Methode verfügbar macht . Dann allesCallable t1
hat zu tun istinvokeAndWait()
fürCallable t2
vor der Rückkehr während seiner Lebensdauer, und umgekehrt.sleep
ist langweilig. Ich glaube zwar, dass 5 Sekunden lang kein Thread startet, aber es ist trotzdem eine Rennbedingung. Sie möchten keinen Programmierer einstellen, auf den Sie sichsleep()
bei der Lösung der Rennbedingungen verlassen können :)Ich habe Yuriy Zubarevs Java-Version des von Eric Lippert veröffentlichten Deadlock-Beispiels umgeschrieben: https://stackoverflow.com/a/9286697/2098232 , um der C # -Version ähnlicher zu werden . Wenn der Initialisierungsblock von Java ähnlich wie der statische C # -Konstruktor funktioniert und zuerst die Sperre abruft, benötigen wir keinen weiteren Thread, um auch die Join-Methode aufzurufen, um einen Deadlock zu erhalten. Es muss nur eine statische Methode aus der Lock-Klasse aufgerufen werden, wie die ursprüngliche C # Beispiel. Der resultierende Deadlock scheint dies zu bestätigen.
quelle
Es ist keine einfachste Interviewaufgabe, die Sie bekommen können: In meinem Projekt hat es die Arbeit eines Teams für einen ganzen Tag gelähmt. Es ist sehr einfach, Ihr Programm zum Stoppen zu bringen, aber es ist sehr schwierig, es in den Zustand zu bringen, in dem der Thread-Dump so etwas schreibt wie:
Das Ziel wäre also, einen Deadlock zu erreichen, den JVM als Deadlock betrachtet. Offensichtlich keine Lösung wie
wird in diesem Sinne funktionieren, obwohl sie in der Tat für immer aufhören werden. Sich auf eine Rennbedingung zu verlassen, ist ebenfalls keine gute Idee, da Sie während des Interviews normalerweise etwas zeigen möchten, das nachweislich funktioniert, und nicht etwas, das die meiste Zeit funktionieren sollte.
Nun, die
sleep()
Lösung ist in gewissem Sinne in Ordnung. Es ist schwer vorstellbar, dass es nicht funktioniert, aber nicht fair ist (wir sind in einem fairen Sport, nicht wahr?). Die Lösung von @artbristol (meine ist die gleiche, nur verschiedene Objekte als Monitore) ist nett, aber lang und verwendet die neuen Parallelitätsprimitive, um die Threads in den richtigen Zustand zu bringen, was nicht so viel Spaß macht:Ich erinnere mich, dass die
synchronized
-only-Lösung für 11..13 Codezeilen (ohne Kommentare und Importe) passt, muss mich aber noch an den eigentlichen Trick erinnern. Wird aktualisiert, wenn ich es tue.Update: Hier ist eine hässliche Lösung für
synchronized
:Beachten Sie, dass wir einen Latch durch einen Objektmonitor ersetzen (
"a"
als Objekt verwenden).quelle
LOCKED
undwaiting to lock
ist subtil, nicht etwas, das Sie während des Frühstücks lesen. Aber du hast wahrscheinlich recht. Lassen Sie mich umformulieren.Diese C # -Version sollte Java wohl ziemlich ähnlich sein.
quelle
console
Anweisungen entfernen . Sie können einfach die gesamteMain
Funktion als schreibenThread.CurrentThread.Join();
.Das Programm blockiert immer, da jeder Thread an der Barriere auf die anderen Threads wartet. Um jedoch auf die Barriere zu warten, muss der Thread den Monitor eingeschaltet halten
BadRunnable.class
.quelle
} catch (InterruptedException ex) { continue; }
... schönHier gibt es ein Beispiel in Java
http://baddotrobot.com/blog/2009/12/24/deadlock/
Wenn ein Entführer in eine Sackgasse gerät, wenn er sich weigert, das Opfer aufzugeben, bis er das Geld erhält, der Verhandlungsführer sich jedoch weigert, das Geld aufzugeben, bis er das Opfer erhält.
quelle
Eine einfache Suche ergab den folgenden Code:
Quelle: Deadlock
quelle
Hier ist ein Beispiel, in dem eine Thread-Haltesperre einen anderen Thread startet, der dieselbe Sperre wünscht, und der Starter dann wartet, bis der Start beendet ist ... für immer:
quelle
Hier ist ein Beispiel:
Es werden zwei Threads ausgeführt, von denen jeder darauf wartet, dass der andere die Sperre aufhebt
öffentliche Klasse ThreadClass erweitert Thread {
}}
Das Ausführen würde zu einem Deadlock führen:
öffentliche Klasse SureDeadlock {
}}
quelle