Lebenszyklus eines Threads in Java

1. Einleitung

In diesem Artikel werden wir ein Kernkonzept in Java ausführlich diskutieren - den Lebenszyklus eines Threads.

Wir werden ein kurzes illustriertes Diagramm und natürlich praktische Codefragmente verwenden, um diese Zustände während der Thread-Ausführung besser zu verstehen.

Um mit dem Verständnis von Threads in Java zu beginnen, ist dieser Artikel zum Erstellen eines Threads ein guter Ausgangspunkt.

2. Multithreading in Java

In der Java-Sprache wird Multithreading vom Kernkonzept eines Threads gesteuert . Während ihres Lebenszyklus durchlaufen Threads verschiedene Zustände:

3. Lebenszyklus eines Threads in Java

Die Klasse java.lang.Thread enthält eine statische Statusaufzählung, die ihre potenziellen Status definiert. Zu einem bestimmten Zeitpunkt kann sich der Thread nur in einem der folgenden Zustände befinden:

  1. NEU - Ein neu erstellter Thread, der die Ausführung noch nicht gestartet hat
  2. RUNNABLE - entweder ausgeführt oder zur Ausführung bereit, wartet jedoch auf die Ressourcenzuweisung
  3. BLOCKED - Warten auf eine Monitorsperre, um einen synchronisierten Block / eine synchronisierte Methode einzugeben oder erneut einzugeben
  4. WARTEN - Warten auf einen anderen Thread, um eine bestimmte Aktion ohne zeitliche Begrenzung auszuführen
  5. TIMED_WAITING - Warten auf einen anderen Thread, um eine bestimmte Aktion für einen bestimmten Zeitraum auszuführen
  6. TERMINATED - hat die Ausführung abgeschlossen

Alle diese Zustände sind in der obigen Abbildung dargestellt. Lassen Sie uns nun jeden dieser Punkte im Detail besprechen.

3.1. Neu

Ein NEUER Thread (oder ein geborener Thread ) ist ein Thread, der erstellt, aber noch nicht gestartet wurde. Es bleibt in diesem Zustand, bis wir es mit der start () -Methode starten .

Das folgende Codefragment zeigt einen neu erstellten Thread im Status NEW :

Runnable runnable = new NewState(); Thread t = new Thread(runnable); Log.info(t.getState());

Da wir den genannten Thread nicht gestartet haben, wird die Methode t.getState () gedruckt:

NEW

3.2. Runnable

Wenn wir einen neuen Thread erstellt und die start () -Methode aufgerufen haben , wird er vom Status NEW in den Status RUNNABLE verschoben . Threads in diesem Status werden entweder ausgeführt oder können ausgeführt werden, warten jedoch auf die Ressourcenzuweisung vom System.

In einer Umgebung mit mehreren Threads weist der Thread-Scheduler (der Teil von JVM ist) jedem Thread eine feste Zeitspanne zu. Es wird also für eine bestimmte Zeit ausgeführt und gibt das Steuerelement dann an andere RUNNABLE- Threads weiter.

Fügen wir beispielsweise die Methode t.start () zu unserem vorherigen Code hinzu und versuchen, auf den aktuellen Status zuzugreifen:

Runnable runnable = new NewState(); Thread t = new Thread(runnable); t.start(); Log.info(t.getState());

Dieser Code gibt höchstwahrscheinlich die Ausgabe zurück als:

RUNNABLE

Beachten Sie, dass in diesem Beispiel nicht immer garantiert ist, dass sich unser Steuerelement zum Zeitpunkt des Erreichens von t.getState () noch im Status RUNNABLE befindet .

Es kann vorkommen, dass es sofort vom Thread-Scheduler geplant wurde und die Ausführung beendet. In solchen Fällen erhalten wir möglicherweise eine andere Ausgabe.

3.3. verstopft

Ein Thread befindet sich im Status BLOCKED, wenn er derzeit nicht ausgeführt werden kann. Es tritt in diesen Zustand ein, wenn es auf eine Monitorsperre wartet und versucht, auf einen Codeabschnitt zuzugreifen, der von einem anderen Thread gesperrt wird.

Versuchen wir, diesen Zustand zu reproduzieren:

public class BlockedState { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new DemoThreadB()); Thread t2 = new Thread(new DemoThreadB()); t1.start(); t2.start(); Thread.sleep(1000); Log.info(t2.getState()); System.exit(0); } } class DemoThreadB implements Runnable { @Override public void run() { commonResource(); } public static synchronized void commonResource() { while(true) { // Infinite loop to mimic heavy processing // 't1' won't leave this method // when 't2' try to enter this } } }

In diesem Code:

  1. Wir haben zwei verschiedene Threads erstellt - t1 und t2
  2. t1 startet und gibt die synchronisierte commonResource () -Methode ein; Dies bedeutet, dass nur ein Thread darauf zugreifen kann. Alle anderen nachfolgenden Threads, die versuchen, auf diese Methode zuzugreifen, werden für die weitere Ausführung gesperrt, bis der aktuelle Thread die Verarbeitung beendet
  3. Wenn t1 in diese Methode eintritt, wird sie in einer Endlosschleife gehalten; Dies dient nur dazu, eine starke Verarbeitung zu imitieren, damit nicht alle anderen Threads in diese Methode eintreten können
  4. Wenn wir nun t2 starten , versucht es, die commonResource () -Methode einzugeben , auf die t1 bereits zugreift , sodass t2 im BLOCKED- Zustand gehalten wird

In diesem Zustand rufen wir t2.getState () auf und erhalten die Ausgabe wie folgt :

BLOCKED

3.4. Warten

Ein Thread befindet sich im Status WAITING, wenn er darauf wartet, dass ein anderer Thread eine bestimmte Aktion ausführt. Laut JavaDocs kann jeder Thread in diesen Status wechseln, indem er eine der folgenden drei Methoden aufruft:

  1. object.wait ()
  2. thread.join () oder
  3. LockSupport.park ()

Beachten Sie, dass wir in wait () und join () keine Zeitüberschreitung definieren, da dieses Szenario im nächsten Abschnitt behandelt wird.

Wir haben eine eigene Tutorial , dass bespricht ausführlich die Verwendung von wait () , notify () und notifyAll () .

Versuchen wir zunächst, diesen Zustand zu reproduzieren:

public class WaitingState implements Runnable { public static Thread t1; public static void main(String[] args) { t1 = new Thread(new WaitingState()); t1.start(); } public void run() { Thread t2 = new Thread(new DemoThreadWS()); t2.start(); try { t2.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.error("Thread interrupted", e); } } } class DemoThreadWS implements Runnable { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.error("Thread interrupted", e); } Log.info(WaitingState.t1.getState()); } }

Lassen Sie uns diskutieren, was wir hier machen:

  1. Wir haben den t1 erstellt und gestartet
  2. t1 erstellt ein t2 und startet es
  3. Während die Verarbeitung von t2 fortgesetzt wird, rufen wir t2.join () auf , wodurch t1 in den Status WAITING versetzt wird, bis t2 die Ausführung beendet hat
  4. Da t1 auf den Abschluss von t2 wartet , rufen wir t1.getState () von t2 aus auf

Die Ausgabe hier ist wie erwartet:

WAITING

3.5. Zeitgesteuertes Warten

Ein Thread befindet sich im Status TIMED_WAITING, wenn er darauf wartet, dass ein anderer Thread innerhalb einer festgelegten Zeit eine bestimmte Aktion ausführt.

According to JavaDocs, there are five ways to put a thread on TIMED_WAITING state:

  1. thread.sleep(long millis)
  2. wait(int timeout) or wait(int timeout, int nanos)
  3. thread.join(long millis)
  4. LockSupport.parkNanos
  5. LockSupport.parkUntil

To read more about the differences between wait() and sleep() in Java, have a look at this dedicated article here.

For now, let's try to quickly reproduce this state:

public class TimedWaitingState { public static void main(String[] args) throws InterruptedException { DemoThread obj1 = new DemoThread(); Thread t1 = new Thread(obj1); t1.start(); // The following sleep will give enough time for ThreadScheduler // to start processing of thread t1 Thread.sleep(1000); Log.info(t1.getState()); } } class DemoThread implements Runnable { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.error("Thread interrupted", e); } } }

Here, we've created and started a thread t1 which is entered into the sleep state with a timeout period of 5 seconds; the output will be:

TIMED_WAITING

3.6. Terminated

This is the state of a dead thread. It's in the TERMINATED state when it has either finished execution or was terminated abnormally.

We have a dedicated article that discusses different ways of stopping the thread.

Let's try to achieve this state in the following example:

public class TerminatedState implements Runnable { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new TerminatedState()); t1.start(); // The following sleep method will give enough time for // thread t1 to complete Thread.sleep(1000); Log.info(t1.getState()); } @Override public void run() { // No processing in this block } }

Here, while we've started thread t1, the very next statement Thread.sleep(1000) gives enough time for t1 to complete and so this program gives us the output as:

TERMINATED

In addition to the thread state, we can check the isAlive() method to determine if the thread is alive or not. For instance, if we call the isAlive() method on this thread:

Assert.assertFalse(t1.isAlive());

Es wird false zurückgegeben. Einfach ausgedrückt, ein Thread lebt nur dann, wenn er gestartet wurde und noch nicht gestorben ist.

4. Fazit

In diesem Tutorial haben wir den Lebenszyklus eines Threads in Java kennengelernt. Wir haben uns alle sechs von Thread.State enum definierten Zustände angesehen und sie mit kurzen Beispielen reproduziert.

Obwohl die Codefragmente auf fast jedem Computer dieselbe Ausgabe liefern, erhalten wir in einigen Ausnahmefällen möglicherweise unterschiedliche Ausgaben, da das genaue Verhalten von Thread Scheduler nicht bestimmt werden kann.

Und wie immer sind die hier verwendeten Codefragmente auf GitHub verfügbar.