Java Multithreading Basics-13: Ein Artikel, der die Ursachen und Lösungen von Deadlocks erläutert

Ein Deadlock ist eine Situation, in der mehrere Threads gleichzeitig blockiert sind und einer oder alle davon auf die Freigabe einer Ressource warten. Da der Thread auf unbestimmte Zeit blockiert ist, kann das Programm nicht ordnungsgemäß beendet werden.

Inhaltsverzeichnis

1. Mehrere Deadlock-Situationen

1. Ein Thread, eine Sperre (derselbe Thread sperrt dasselbe Objekt oben zweimal)

2. Zwei Threads, zwei Schlösser

3. N-Threads und M-Sperren

2. Vier notwendige Bedingungen, um einen Deadlock zu verursachen⭐

3. So vermeiden Sie einen Deadlock

1. Sperrsequenz – Unterbrechung der Warteschleife

2. Ressourcenallokationsstrategie – Banker-Algorithmus (weggelassen)

3. Timeout-Mechanismus

4. Erkennung und Wiederherstellung von Deadlocks

5. Vermeiden Sie die gemeinsame Nutzung von Ressourcen

4. Stillstand bei Interviewfragen


1. Mehrere Deadlock-Situationen

1. Ein Thread, eine Sperre (derselbe Thread sperrt dasselbe Objekt zweimal)

Wiedereintrittssperren sind in Ordnung, aber nicht wiedereintrittsfähige Sperren können zu einem Deadlock führen.

class BlockingQueue {
    synchronized void put(int elem){
        this.size();
        ...
    }
    
    synchronized int size() {
        ...
    }
}

Ergänzung: Wiedereintretende Sperren und nicht wiedereintretende Sperren 

Wiedereintrittssperre

Es handelt sich um eine Sperre, die denselben Thread dabei unterstützt, die Sperre mehrmals zu erhalten. Wenn ein Thread zum ersten Mal eine Wiedereintrittssperre erhält, kann er die Sperre mehrmals erwerben, ohne durch die von ihm gehaltene Sperre blockiert zu werden.

Wenn beispielsweise eine Sperroperation in einer rekursiven Funktion vorhanden ist, blockiert sich die Sperre dann selbst während des rekursiven Prozesses? Wenn nicht, handelt es sich bei der Sperre um eine Wiedereintrittssperre ( Wiedereintrittssperren werden aus diesem Grund auch als rekursive Sperren bezeichnet ) .

In Java ist jede Sperre , deren Name mit „Reentrant“ beginnt, eine wiedereintrittsfähige Sperre, und alle vom JDK bereitgestellten vorgefertigten Sperrimplementierungsklassen , einschließlich synchronisierter Schlüsselwortsperren, sind wiedereintrittsfähig. Wenn eine wiedereintretende Sperre gesperrt wird, wird ermittelt, ob der Thread, der die Sperre derzeit beantragt, bereits der Eigentümer der Sperre ist. Wenn ja, wird sie direkt freigegeben.

Keine Wiedereintrittssperre

Es handelt sich um eine Sperre, die nicht zulässt, dass derselbe Thread die Sperre mehrmals erhält. Wenn ein Thread zum ersten Mal eine nicht wiedereintrittsfähige Sperre erwirbt, wird er durch die Sperre, die er hält, blockiert, wenn er versucht, die Sperre erneut zu erlangen. Wenn derselbe Thread versucht, die Sperre erneut zu erhalten, nachdem er die nicht wiedereintrittsfähige Sperre erworben hat, führt er zu einem Deadlock, indem er sich „selbst sperrt“. 

2. Zwei Threads, zwei Schlösser

Sogar wiederkehrende Sperren können zu einem Deadlock führen. Wie in der folgenden Abbildung dargestellt, werden t1 und t2 gleichzeitig ausgeführt. t1 sperrt zuerst Locker1 und t2 sperrt zuerst Locker2; t1 setzt die Ausführung fort und sperrt Locker2 erneut, muss aber warten, bis t2 zuerst Locker2 freigibt; t2 setzt die Ausführung fort und sperrt Locker1 erneut, muss aber warten, bis t1 zuerst Locker1 freigibt. Zu diesem Zeitpunkt tritt ein Deadlock auf.

Das ist gleichbedeutend damit, während der Epidemie in den Supermarkt zu gehen, um Masken ohne Maske zu kaufen, aber der Supermarkt sagt, man hat keine Maske und lässt einen nicht rein.

3. N-Threads und M-Sperren

Mit zunehmender Anzahl von Threads und Sperren kann es leichter zu Deadlocks kommen.

Dabei handelt es sich um das berühmte „Problem der Speisephilosophen“:

Wenn fünf Philosophen gleichzeitig die Stäbchen auf der linken Seite in die Hand nehmen, kommt es offensichtlich zu einer Sackgasse. Mit anderen Worten: Wenn ein Thread zwei Sperren hinzufügen möchte, eine bereits hinzugefügt wurde und die andere entfernt wurde, wartet er weiter, während er die erste Sperre belegt.


2. Vier notwendige Bedingungen, um einen Deadlock zu verursachen⭐

  1. Sich gegenseitig ausschließende Verwendung: Das heißt, wenn eine Ressource von einem Thread verwendet (belegt) wird, können andere Threads sie nicht verwenden. [Grundlegende Eigenschaften von Schlössern]
  2. Nicht präventiv: Ressourcenanforderer können Ressourcen nicht gewaltsam von Ressourcennutzern beschlagnahmen, und Ressourcen können nur aktiv von Ressourcennutzern freigegeben werden. [Grundlegende Eigenschaften von Schlössern]
  3. Anfordern und Halten: Das heißt, wenn ein Ressourcenanforderer andere Ressourcen anfordert, behält er den Besitz der ursprünglichen Ressource. (Iss das Essen in der Schüssel und schau dir das Essen im Topf an.) (Du wirst das vorherige nicht aufgeben, auch wenn du nicht das zweite Essstäbchen bekommst.) [Eigenschaften des Codes]
  4. Zirkuläres Warten: Es gibt eine Warteschlange: P1 belegt die Ressourcen von P2, P2 belegt die Ressourcen von P3 und P3 belegt die Ressourcen von P1. Dadurch entsteht eine Warteschleife mit logischen Abhängigkeiten. (Der Hausschlüssel verschließt das Auto und der Autoschlüssel verschließt das Haus.) (Im obigen Philosophen wartet 5 auf 4, 4 wartet auf 3,..., 2 wartet auf 1 und 1 wartet auf 5) [Eigenschaften des Codes]

3. So vermeiden Sie einen Deadlock

Es gibt viele Möglichkeiten, einen Deadlock zu vermeiden. Hier sind nur fünf aufgeführt. Dabei liegt der Schwerpunkt auf dem ersten Punkt: der Sperrsequenz.

1. Sperrsequenz – Unterbrechung der Warteschleife

Ein Deadlock entsteht, wenn die oben genannten vier Bedingungen erfüllt sind. Im Falle eines Deadlocks verschwindet der Deadlock, wenn eine der oben genannten Bedingungen verletzt wird. Eine der am einfachsten zu durchbrechenden Methoden ist das „Loop Wait“.

Eine einfache Möglichkeit, die zyklische Wartesituation zu lösen, besteht darin, die Sperren zu nummerieren. Wenn Sie mehrere Sperren gleichzeitig erwerben müssen, muss die Sperrreihenfolge darin bestehen, zuerst die kleine Zahl und dann die große Zahl zu sperren.

Wie in der Abbildung unten gezeigt, besteht Einigkeit darüber, dass jeder Philosoph nur die Stäbchen mit kleineren Zahlen in die linke Hand und die rechte Hand zuerst nehmen kann. Philosoph Nr. 2 bekommt als erster das Stäbchen Nr. 1, und die übrigen Philosophen bekommen abwechselnd auch die kleineren Stäbchen zwischen ihre linke und rechte Hand. Wenn der letzte Philosoph Nr. 1 an der Reihe ist, da Stäbchen Nr. 1 bereits besetzt ist , er kann Essstäbchen 1 nicht bekommen. Nein. Essstäbchen, während er auf die Blockierung wartet.

Während Philosoph Nr. 1 blockiert ist und wartet, kann Philosoph Nr. 5 Stäbchen Nr. 5 in die Hand nehmen und mit dem Nudelnessen beginnen. Als er die Aufgabe erledigt hatte, legte er die beiden Stäbchen Nr. 4 und Nr. 5 ab. Zu diesem Zeitpunkt konnte der Philosoph Nr. 4 die Stäbchen Nr. 4 aufheben, um Nudeln zu essen. Die Philosophen Nr. 3 und Nr. 2 wiederum können auch Nudeln zu Ende essen. Nachdem Philosoph Nr. 2 seine Essstäbchen abgelegt hat, kann Philosoph Nr. 1 Essstäbchen Nr. 1 und Essstäbchen Nr. 5 erhalten, wodurch das blockierende Warten beendet und mit der Ausführung der Aufgabe „Nudeln essen“ begonnen wird.

Solange die Sperrsequenz vereinbart ist, wird daher die Schleifenwartebedingung natürlich unterbrochen und es entsteht kein Deadlock. Wie aus dem Code hervorgeht, muss auf die Reihenfolge der Sperren geachtet werden, solange einem Thread mehrere Sperren hinzugefügt werden. Sie können vereinbaren, dass bei jedem Sperren zuerst die kleinere Zahl und dann die größere Zahl gesperrt wird und alle Threads dieser Reihenfolge folgen.

Wie in der Abbildung unten gezeigt, gilt: Schließen Sie jedes Mal, wenn Sie es abschließen, zuerst Schließfach1 und dann Schließfach2 ab. (Legen Sie die Reihenfolge der Gewindesicherung fest.)

2. Ressourcenallokationsstrategie – Banker-Algorithmus (weggelassen)

Ressourcenzuweisungsstrategien wie der Banker -Algorithmus können verwendet werden , um den maximalen Bedarf und die maximale Verfügbarkeit von Ressourcen vorab zu bewerten, um sicherzustellen, dass das System bei der Zuweisung von Ressourcen keinen Deadlock verursacht.

Das Wesentliche des Bankalgorithmus ist eine vernünftigere Ressourcenallokation. Sie lernen es im Betriebssystemkurs der Schule und es ist auch eine Pflichtfrage in der Abschlussprüfung.

Tatsächlich ist es jedoch relativ komplex. Die Implementierung dieses Algorithmus selbst kann zu zusätzlichen Fehlern führen, was den Verlust nicht wert ist und daher nicht für die Verwendung in der tatsächlichen Entwicklung geeignet ist. Ich werde hier nicht auf Details eingehen.

3. Timeout-Mechanismus

Legen Sie eine Zeitüberschreitung für den Sperrenerfassungsvorgang fest, geben Sie den Erwerb der Sperre auf, nachdem Sie länger als eine bestimmte Zeit gewartet haben, und führen Sie die entsprechende Verarbeitung durch, um eine Systemblockierung durch langes Warten zu vermeiden.

4. Erkennung und Wiederherstellung von Deadlocks

Indem regelmäßig erkannt wird, ob im System ein Deadlock vorliegt, und geeignete Maßnahmen zur Wiederherstellung ergriffen werden, wie z. B. das Beenden bestimmter Prozesse oder das Zurücksetzen von Vorgängen.

5. Vermeiden Sie die gemeinsame Nutzung von Ressourcen

Minimieren Sie die Anzahl gemeinsam genutzter Ressourcen zwischen Prozessen oder verwenden Sie Kopien anstelle gemeinsam genutzter Ressourcen, um die Möglichkeit eines Deadlocks durch Ressourcenkonkurrenz zu vermeiden.


4. Stillstand bei Interviewfragen

Lassen Sie uns darüber sprechen, was Deadlock ist, wie man Deadlocks vermeidet und wie man Algorithmen vermeidet. Ist es tatsächlich gelöst?

Zusammenfassen:

Deadlock bezieht sich auf einen Zustand, in dem zwei oder mehr Threads (oder Prozesse, im Folgenden werden nur Threads beschrieben) unbegrenzt auf voneinander gehaltene Ressourcen warten, was dazu führt, dass das Programm die Ausführung nicht fortsetzen kann.

Die vier notwendigen Bedingungen für das Auftreten eines Deadlocks sind: gegenseitige Ausschlussbedingungen (Ressourcen können jeweils nur von einem Thread belegt werden), Anforderungs- und Haltebedingungen (Threads, die Ressourcen enthalten, warten auch darauf, von anderen Threads gehaltene Ressourcen abzurufen), Nicht präemptiv Bedingungen (Threads können nicht zwangsweise von Ressourcen befreit werden, die bereits von anderen Threads belegt sind), zirkuläre Wartebedingungen (jeder Thread wartet auf Ressourcen, die vom nächsten Thread gehalten werden).

Um Deadlocks zu vermeiden, können Sie eine feste Sperrsequenz übernehmen, die Ressourcenzuteilungsstrategie anpassen, ein Zeitlimit für die Freigabe der Sperre festlegen, Deadlocks regelmäßig erkennen, die gemeinsame Nutzung von Ressourcen vermeiden und andere Strategien anwenden. Vermeidungsalgorithmen sind eine Methode zur Verhinderung von Deadlocks. Die bekannteste davon ist der Banker-Algorithmus. Der Bankalgorithmus basiert auf der Sicherheit der Ressourcenzuweisung und stellt sicher, dass es bei der Ressourcenzuweisung nicht zu Deadlocks kommt.

Supongo que te gusta

Origin blog.csdn.net/wyd_333/article/details/131737361
Recomendado
Clasificación