Verstehen Sie den Fehler, indem Sie ihn implementieren und analysieren. (1) Deadlock (Java)

Überblick

Es gibt viele Möglichkeiten, Probleme wie Speicherlecks und Deadlocks im Internet zu analysieren. Da jedoch nicht bekannt ist, wann und welche Art von Problem vor Ihnen auftreten wird, lernen und üben Sie die Analysemethode systematisch, bevor Sie sich dem Problem tatsächlich stellen. Ich denke, es ist ziemlich schwierig, es zu behalten.

Aus diesem Grund haben wir eine mit Fehlern gefüllte Webanwendung entwickelt, die das Problem absichtlich reproduzieren kann, damit wir es in einem praktischen Format lernen können, und auch [It] für Qiita. Informationen zu](http://qiita.com/tamura__246/items/7275c7406706fb0057e8) wurden veröffentlicht. Diese Webanwendung ist sehr einfach zu erstellen und mit einem einzigen Befehl zu starten. Ich denke, sie kann auch für experimentelle und pädagogische Zwecke verwendet werden.

Mit dieser Webanwendung möchte ich verschiedene Probleme (Bugs) vorstellen und nach und nach analysieren.

Der erste Fehler, den ich einführen werde, ist ein Java-Thread ** Deadlock **.

Reproduzieren Sie das tote Schloss

Lassen Sie uns zunächst das tote Schloss reproduzieren. Laden Sie die Webanwendung herunter und starten Sie sie (https://github.com/k-tamura/easybuggy/releases/latest).

$ java -jar easybuggy.jar

Oder

$ git clone https://github.com/k-tamura/easybuggy
$ cd easybuggy
$ mvn clean install exec:exec

Sie können es auch starten, indem Sie ROOT.war in einem Servlet-Container wie Tomcat bereitstellen. Wenn die folgende Meldung angezeigt wird, wurde die Webanwendung gestartet.

5 27, 2017 3:29:58 Uhr org.apache.coyote.AbstractProtocol start
Information: Starting ProtocolHandler ["http-bio-8080"]
> set path=%path%;C:\Program Files\Java\jdk1.8.0_121\bin
> "C:\Program Files\Java\jdk1.8.0_121\jre\bin\java" -jar easybuggy.jar

Greifen Sie nach dem Start auf die folgende URL zu.

http://localhost:8080

Wenn es normal startet, wird der folgende Bildschirm angezeigt.

mainpage_ja.png

Klicken Sie dort auf den Link "Dead Lock (Java)". Dann sehen Sie einen Bildschirm, auf dem nur steht: "Das mehrmalige Laden dieser Seite führt zu einem Deadlock." Versuchen Sie zunächst, den Bildschirm zu laden, indem Sie die Taste F5 nur einmal drücken. Nach ca. 5 Sekunden wird eine Antwort zurückgegeben und die Meldung "Es ist kein Deadlock aufgetreten" angezeigt. Deadlocks treten nicht in Situationen auf, in denen nur eine einzige Anforderung verarbeitet wird. Drücken Sie dann zweimal hintereinander die Taste F5. Egal wie lange Sie warten, Sie sollten diesmal keine Antwort erhalten. Zu diesem Zeitpunkt hat die Webanwendung eine Deadlock.

Analysieren

Wenn keine Antwort zurückgegeben wird, sind die folgenden Möglichkeiten möglich.

-Die Verarbeitung wird gestoppt (verursacht durch Dead Lock usw.) -Die Verarbeitung ist nicht abgeschlossen (verursacht durch Endlosschleife usw.) -Die Verarbeitung dauert länger als erwartet (aufgrund einer Logik, bei der die Leistung nicht berücksichtigt wird usw.)

Wenn Sie keine Antwort erhalten, sollten Sie als Erstes einen Thread-Dump erstellen.

Verwenden Sie zunächst den Befehl jps, um die Prozess-ID der Webanwendung zu identifizieren.

$ jps
85489 org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
45733 Launcher
69970 easybuggy.jar
89901 Jps

Der Prozessname dieser Webanwendung lautet "easybuggy.jar", wenn sie mit dem Befehl java gestartet wird, und "EmbeddedJettyServer", wenn sie mit dem Befehl mvn gestartet wird. Im obigen Fall wäre 69970 die Prozess-ID.

Wenn Sie den Befehl jstack mit der angegebenen Prozess-ID ausführen, wird ein Thread-Dump ausgegeben.

$ jstack 69970
2017-02-26 18:58:57
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):

"Attach Listener" #71 daemon prio=9 os_prio=0 tid=0x00007f4804001800 nid=0x1120f runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"derby.rawStoreDaemon" #70 daemon prio=5 os_prio=0 tid=0x00007f4798deb800 nid=0x111b2 in Object.wait() [0x00007f47e0473000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	at org.apache.derby.impl.services.daemon.BasicDaemon.rest(Unknown Source)
	- locked <0x00000000f7aa3a80> (a org.apache.derby.impl.services.daemon.BasicDaemon)
	at org.apache.derby.impl.services.daemon.BasicDaemon.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)

・ ・ ・(Abkürzung)・ ・ ・

Found one Java-level deadlock:
=============================
"http-listener(8)":
  waiting to lock monitor 0x00007f4830363368 (object 0x00000000f0165dc8, a java.lang.Object),
  which is held by "http-listener(9)"
"http-listener(9)":
  waiting to lock monitor 0x00007f4830364b78 (object 0x00000000f0165db8, a java.lang.Object),
  which is held by "http-listener(8)"

Java stack information for the threads listed above:
===================================================
"http-listener(8)":
	at org.t246osslab.easybuggy.troubles.DeadlockServlet.lock12(DeadlockServlet.java:61)
	- waiting to lock <0x00000000f0165dc8> (a java.lang.Object)
	- 
	- locked <0x00000000f0165db8> (a java.lang.Object)
	at org.t246osslab.easybuggy.troubles.DeadlockServlet.doGet(DeadlockServlet.java:43)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)

・ ・ ・(Abkürzung)・ ・ ・

"http-listener(9)":
	at org.t246osslab.easybuggy.troubles.DeadlockServlet.lock21(DeadlockServlet.java:70)
	- waiting to lock <0x00000000f0165db8> (a java.lang.Object)
	- locked <0x00000000f0165dc8> (a java.lang.Object)
	at org.t246osslab.easybuggy.troubles.DeadlockServlet.doGet(DeadlockServlet.java:46)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)

・ ・ ・(Abkürzung)・ ・ ・

Found 1 deadlock.

Sie können sehen, dass der Deadlock aufgetreten ist, da "Ein Deadlock auf Java-Ebene gefunden:" angezeigt wird. Deadlocks können leicht durch Thread-Dumps erkannt werden und können auch identifizieren, welcher Thread das Problem verursacht. Das Obige zeigt an, dass zwischen den beiden Threads "http-Listener (8)" und "http-Listener (9)" ein Deadlock besteht. Sie können sehen, dass der erstere darauf wartet, in Zeile 61 von DeadlockServlet.java gesperrt zu werden, und der letztere in Zeile 70 von DeadlockServlet.java.

Was ist Dead Lock?

Was für ein Ereignis ist überhaupt ein Deadlock? Dead Lock bedeutet, dass mehrere Prozesse (in diesem Fall Threads) auf die Freigabe von Ressourcen (in diesem Fall Objekten) warten, die voneinander belegt (gesperrt) sind, und die Verarbeitung stoppt.

Nachdem beispielsweise Thread 1 Objekt 1 und Thread 2 Objekt 2 gesperrt hat, werden beide nicht entsperrt, aber Thread 1 sperrt Objekt 2 und Thread 2 sperrt Objekt 1 wie unten gezeigt. Wenn Sie es versuchen, warten beide Threads aufeinander und der Vorgang wird gestoppt. Dieser Zustand ist Dead Lock.

a691a79f-30c0-33e3-9731-d0a6814d5fc0.png

Um welche Art von Implementierung handelt es sich?

Was war die Implementierung des Quellcodes? DeadlockServlet.java, das zuvor auf dem Thread-Dump ausgegeben wurde Schauen wir uns das an. Die wichtigen Teile sind:

    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    private boolean switchFlag = true;

    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

・ ・ ・(Abkürzung)・ ・ ・

                switchFlag = !switchFlag;
                if (switchFlag) {
                    lock12();
                } else {
                    lock21();
                }

・ ・ ・(Abkürzung)・ ・ ・

    }

    private void lock12() {
        synchronized (lock1) {
            sleep();
            synchronized (lock2) {
                sleep();
            }
        }
    }

    private void lock21() {
        synchronized (lock2) {
            sleep();
            synchronized (lock1) {
                sleep();
            }
        }
    }

In diesem Servlet wechselt ein Flag namens switchFlag die Anforderungen zwischen den beiden Methoden lock12 () und lock21 (). In lock12 () ist das Objekt "lock1" gesperrt → 5 Sekunden lang schlafen → das Objekt "lock2" ist in dieser Reihenfolge gesperrt. In lock21 () wird die Verarbeitung in umgekehrter Reihenfolge ausgeführt.

Daher ist dieser Fall wie folgt.

dlockinapp.png

Zwei HTTP-Listener-Threads (http-Listener (8) und http-Listener (9)), die zum Akzeptieren von Anforderungen von Clients erstellt wurden, warten auf die Freigabe ihrer jeweiligen gesperrten Objekte (lock1, lock2). Deshalb ist es ein totes Schloss.

Andere Erkennungsmethoden als Thread-Dump

Es gibt andere Möglichkeiten, Deadlocks zu erkennen.

VisualVM

Sie können Deadlocks auch mithilfe der Visual VM erkennen, die mit dem JDK von Oracle geliefert wird. VisualVM erkennt automatisch Deadlocks und zeigt eine Warnung in Rot an.

visualvm.png

Java Mission Control

Wenn Sie Java Mission Control verwenden, das auch mit dem JDK von Oracle geliefert wird, können Sie es erkennen, indem Sie das Kontrollkästchen "Deadlock-Erkennung" aktivieren.

JMC.png

JConsole

Das OpenJDK verfügt nicht über eine Visual VM oder Java Mission Control, sondern über eine JConsole. Sie können dies auch erkennen, indem Sie auf die Schaltfläche "Dead Lock erkennen" klicken.

JConsole.png

Kann die JVM einen Deadlock erkennen und einen Thread beenden?

Im Allgemeinen kann RDBMS, wenn eine Dead Lock erkannt wird, eine Transaktion zurückgesetzt und die andere festgeschrieben werden. Kann die JVM dasselbe tun? Die Antwort ist nein **". Es gibt keine solche JVM-Option. Die einzige Möglichkeit, eine Webanwendung nach einem Deadlock wiederherzustellen, ist ein Neustart. Im Allgemeinen tauchen Deadlocks selten sofort auf, sodass es vorkommen kann, dass eine große Anzahl von Threads wartet. Daher muss der Programmierer beim Implementieren des Prozesses zum Sperren eines Objekts ** darauf achten, Inkonsistenzen in der Prozessreihenfolge ** zu vermeiden.

Deadlock programmgesteuert erkennen

Sie können Deadlocks programmgesteuert erkennen. Sie können java.lang.management.ThreadMXBean.findDeadlockedThreads () verwenden, um Informationen zu festgefahrenen Threads abzurufen (siehe unten).

        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        long[] threadIds = bean.findDeadlockedThreads();
        if (threadIds != null) {
            ThreadInfo[] infos = bean.getThreadInfo(threadIds);
            for (ThreadInfo info : infos) {
                log.error(info.toString());
            }
        }

Diese Zusammenfassung


Referenz: COMP 3400 Vorlesung 7: Deadlock (Vorlesungsmaterial von der Ottervine University?) http://faculty.otterbein.edu/PSanderson/COMP3400/notes/lecture07.html


[^ 1]: Wahrscheinlich aufgrund eines Fehlers in Payara Micro, aber die Ursache wurde nicht identifiziert. Ich werde es nachschlagen, wenn ich Zeit habe.

Recommended Posts

Verstehen Sie den Fehler, indem Sie ihn implementieren und analysieren. (1) Deadlock (Java)
Verstehen Sie den Fehler, indem Sie ihn implementieren und analysieren. (2) Deadlock (SQL)
Verstehen Sie das Singleton-Muster, indem Sie Java- und JavaScript-Code vergleichen
Verstehen Sie das Iterator-Muster, indem Sie Java- und JavaScript-Code vergleichen
Java als Wert übergeben und als Referenz übergeben
[Java] Verstehe den Unterschied zwischen List und Set
[Swift vs Java] Lassen Sie uns statisch und endgültig verstehen
[Java] Verstehe in 10 Minuten! Assoziatives Array und HashMap