Definition des BLOCKED-Status
Wie bereits erwähnt, lautet die einfache Definition von BLOCKED:
Ein Thread, der blockiert ist und auf eine Monitorsperre wartet, befindet sich in diesem Status. (Ein Thread, der blockiert ist und auf eine Monitorsperre wartet, befindet sich in diesem Zustand.)
Detailliertere Definitionen finden Sie im Javadoc in Thread.State:
/** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED,
Dieser Satz lautet sehr lang, kann zum Verständnis in zwei einfache Sätze zerlegt werden.
Ein Thread im blockierten Zustand wartet auf eine Monitorsperre, um in einen synchronisierten Block/eine synchronisierte Methode einzutreten.
Ein Thread im blockierten Zustand wartet auf eine Monitorsperre, um in einen synchronisierten Block oder eine synchronisierte Methode einzutreten.
Ein Thread im blockierten Zustand wartet auf eine Monitorsperre, um nach dem Aufruf von Object.wait erneut in einen synchronisierten Block/eine synchronisierte Methode einzutreten.
Ein Thread im blockierten Zustand wartet auf eine Monitorsperre, nachdem er die Object.wait-Methode aufgerufen hat, um erneut in einen synchronisierten Block oder eine synchronisierte Methode einzutreten.
Block beim Betreten (Eingeben) des Synchronisationsblocks
Lassen Sie mich mit dem ersten Satz beginnen, der leichter zu verstehen ist.
Monitorsperren werden für den synchronen Zugriff verwendet, um einen gegenseitigen Ausschluss zwischen mehreren Threads zu erreichen. Sobald also ein Thread die Sperre erhält und in den synchronisierten Block eintritt, werden andere Threads, die eintreten möchten, außerhalb des synchronisierten Blocks blockiert, da sie die Sperre nicht erhalten können. Zu diesem Zeitpunkt ist der Status BLOCKIERT.
Hinweis: Der Ein- und Ausgang dieses Zustands unterliegt nicht unserer Kontrolle. Wenn die Sperre verfügbar ist, wird der Thread aus dem Blockierungszustand wiederhergestellt.
Wir können einen Code verwenden, um diesen Prozess zu demonstrieren:
@Test public void testBlocked() throws Exception { class Counter { int counter; public synchronized void increase() { counter++; try { Thread.sleep(30000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } Counter c = new Counter(); Thread t1 = new Thread(new Runnable() { public void run() { c.increase(); } }, "t1线程"); t1.start(); Thread t2 = new Thread(new Runnable() { public void run() { c.increase(); } }, "t2线程"); t2.start(); Thread.sleep(100); // 确保 t2 run已经得到执行 assertThat(t2.getState()).isEqualTo(Thread.State.BLOCKED); }
Das Obige definiert einen Zugriffszählerzähler mit einer synchronen Erhöhungsmethode. Der t1-Thread tritt zuerst in den Synchronisationsblock ein und schläft dann, wodurch die Sperre verzögert wird. Wenn t2 versucht, die Synchronisationsmethode auszuführen, wird er blockiert, da er die Sperre nicht erhalten kann.
VisualVM-Überwachung zeigt den Status des T2-Threads:
Der „Monitor“-Status auf dem Bild ist der Status „BLOCKIERT“. Sie können sehen, dass sich t2 während des Ruhezustands von t1 im Status BLOCKIERT befindet.
Der BLOCKED-Zustand kann als eine besondere Art des Wartens betrachtet werden, insbesondere als Warten auf Sperren.
Blockieren beim erneuten Betreten des Synchronisierungsblocks nach dem Warten
Sehen Sie sich nun noch einmal den zweiten Satz an:
2. Ein Thread im blockierten Zustand wartet auf den erneuten Eintritt einer Monitorsperre ein synchronisierter Block/eine synchronisierte Methode nach dem Aufruf von Object.wait.
Ein Thread im blockierten Zustand wartet auf eine Monitorsperre, nachdem er die Object.wait-Methode aufgerufen hat, um erneut in einen synchronisierten Block oder eine synchronisierte Methode einzutreten.
Dieser Satz ist etwas kompliziert und lässt sich nicht einfach in einen prägnanten chinesischen Satz übersetzen. Es ist nicht einfach, diesen Satz zu verstehen, ohne den relevanten Hintergrund des Wartens genau zu verstehen. Lassen Sie uns hier etwas näher darauf eingehen. Da es sich um eine erneute Eingabe handelt, bedeutet dies, dass es zwei Eingaben gibt:
Der Aufruf der Wartemethode muss in einem synchronisierten Block erfolgen, das heißt, Sie müssen zuerst die Sperre erwerben und den synchronisierten Block eingeben. Dies ist der erste Eintrag.
Nach dem Aufruf von wait wird die Sperre freigegeben und in die Warteschlange (Wartesatz) dieser Sperre eingetragen.
Nachdem der wartende Thread die Notify- oder NotifyAll-Benachrichtigung von anderen Threads erhalten hat, kann er die Ausführung nicht sofort fortsetzen, da sich der Stopp im Synchronisierungsblock befindet und die Sperre aufgehoben wurde. Daher muss er die Sperre erneut erwerben, bevor er erneut gestartet werden kann Geben Sie den synchronisierten Block erneut ein und setzen Sie die Ausführung ab der letzten Wartezeit fort. Dies ist die zweite Eingabe, daher wird dies als erneute Eingabe bezeichnet.
Die Sperre wird ihm jedoch nicht zuerst erteilt. Dieser Vorgang ist tatsächlich derselbe wie der Eingabevorgang, daher kann es auch daran liegen, dass die Sperre vorhanden ist bereits von anderen Threads belegt. Ursachen: BLOCKIERT.
Dieser Vorgang wird als „Neueingabe eines synchronisierten Blocks/einer synchronisierten Methode nach dem Aufruf von Object.wait“ bezeichnet.
Wir verwenden auch einen Code, um diesen Prozess zu demonstrieren:
@Test public void testReenterBlocked() throws Exception { class Account { int amount = 100; // 账户初始100元 public synchronized void deposit(int cash) { // 存钱 amount += cash; notify(); try { Thread.sleep(30000); // 通知后却暂时不退出 } catch (InterruptedException e) { throw new RuntimeException(e); } } public synchronized void withdraw(int cash) { // 取钱 while (cash > amount) { try { wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } amount -= cash; } } Account account = new Account(); Thread withdrawThread = new Thread(new Runnable() { public void run() { account.withdraw(200); } }, "取钱线程"); withdrawThread.start(); Thread.sleep(100); // 确保取钱线程已经得到执行 assertThat(withdrawThread.getState()).isEqualTo(Thread.State.WAITING); Thread depositThread = new Thread(new Runnable() { public void run() { account.deposit(100); } }, "存钱线程"); Thread.sleep(10000); // 让取钱线程等待一段时间 depositThread.start(); Thread.sleep(300); // 确保取钱线程已经被存钱线程所通知到 assertThat(withdrawThread.getState()).isEqualTo(Thread.State.BLOCKED); }
Eine kurze Einführung in das obige Codeszenario:
Es gibt ein Kontoobjekt mit Einzahlung. Bei der Auszahlungsmethode beträgt der Anfangsbetrag 100 Yuan.
Der Geldabhebungsthread startet zuerst und tritt in den (Eingabe-)Synchronisationsblock ein. Er versucht, 200 Yuan abzuheben. Wenn festgestellt wird, dass das Geld nicht ausreicht, wird die Sperre aufgehoben Thread hängt (WAITING-Zustand).
Der Einzahlungsthread startet nach 10 Sekunden, zahlt Geld ein und benachrichtigt den Auszahlungsthread, schläft dann aber weiterhin im Synchronisationsblock, was dazu führt, dass die Sperre nicht aufgehoben wird.
Nach Erhalt der Benachrichtigung verlässt der Auszahlungsthread den Wartezustand, hält die Sperre jedoch nicht mehr aufrecht. Beim Versuch, den Synchronisierungsblock erneut einzugeben, um die Ausführung fortzusetzen, wurde die Sperre vom Einzahlungsthread nicht aufgehoben ist blockiert (BLOCKED-Zustand).
Überwachte Anzeige:
Wie im Bild gezeigt, wartet der Geldabhebungsthread zunächst und blockiert (BLOCKIERT) dann nach Erhalt der Benachrichtigung, da er die Sperre nicht erhalten kann.
Zusammenfassung
Wenn man diese beiden Sätze zusammen betrachtet, haben sie zwei Bedeutungen, aber sie bedeuten immer noch dasselbe: Eingeben, erneut eingeben oder eingeben >
Wenn der synchronisierte Block nicht eingegeben werden kann, weil die Sperre nicht erhalten werden kann, befindet sich der Thread im Status BLOCKIERT. Wenn sich ein Thread längere Zeit im Status BLOCKIERT befindet, prüfen Sie, ob ein Deadlock aufgetreten ist. Der Zustand „BLOCKIERT“ kann als eine besondere Art des Wartens angesehen werden, die eine Unterteilung des traditionellen Wartezustands darstellt: Da es den Zustand „WARTEN“ nicht gibt Da es sich hier um die Wartemethode handelt, haben wir oben eine kleine Analyse des Wartens durchgeführt. Im nächsten Kapitel werden wir die beiden Zustände WAITING und TIMED_WAITING genauer analysieren.