Levenscyclus van een draad in Java

1. Inleiding

In dit artikel bespreken we in detail een kernconcept in Java: de levenscyclus van een thread.

We gebruiken een snel geïllustreerd diagram en natuurlijk praktische codefragmenten om deze toestanden beter te begrijpen tijdens de uitvoering van de thread.

Om te beginnen met het begrijpen van Threads in Java, is dit artikel over het maken van een thread een goede plek om te beginnen.

2. Multithreading in Java

In de Java-taal wordt multithreading aangedreven door het kernconcept van een thread. Tijdens hun levenscyclus doorlopen threads verschillende toestanden:

3. Levenscyclus van een draad in Java

De java.lang.Thread klasse bevat een statische opsomming - die de mogelijke toestanden definieert. Op een bepaald moment kan de thread zich alleen in een van deze toestanden bevinden:

  1. NIEUW - een nieuw gemaakte thread die de uitvoering nog niet heeft gestart
  2. UITVOERBAAR - loopt of is klaar voor uitvoering, maar het wacht op toewijzing van middelen
  3. GEBLOKKEERD - wachten op het verkrijgen van een monitorvergrendeling om een ​​gesynchroniseerd blok / methode in of opnieuw in te voeren
  4. AAN HET WACHTEN - wachten op een andere thread om een ​​bepaalde actie uit te voeren zonder enige tijdslimiet
  5. TIMED_WAITING - wachten op een andere thread om een ​​specifieke actie uit te voeren voor een bepaalde periode
  6. BEËINDIGD - heeft de uitvoering voltooid

Al deze toestanden worden behandeld in het bovenstaande diagram; laten we deze nu allemaal in detail bespreken.

3.1. Nieuw

EEN NIEUWDraad (of een Born Draad) is een thread die is gemaakt maar nog niet is gestart. Het blijft in deze staat totdat we het starten met behulp van de begin() methode.

Het volgende codefragment toont een nieuw gemaakte thread die zich in de NIEUW staat:

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

Omdat we de genoemde thread niet hebben gestart, is de methode t.getState () prenten:

NIEUW

3.2. Runnable

Wanneer we een nieuwe thread hebben gemaakt en de begin() methode daarop, het is verplaatst van NIEUW naar UITVOERBAAR staat. Threads in deze status zijn actief of klaar om te worden uitgevoerd, maar ze wachten op resourcetoewijzing door het systeem.

In een omgeving met meerdere threads wijst de Thread-Scheduler (die deel uitmaakt van JVM) een vaste hoeveelheid tijd toe aan elke thread. Het loopt dus een bepaalde tijd en geeft vervolgens de controle over aan een ander UITVOERBAAR draden.

Laten we bijvoorbeeld toevoegen t.start () methode naar onze vorige code en probeer toegang te krijgen tot de huidige staat:

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

Deze code is hoogstwaarschijnlijk om de uitvoer te retourneren als:

UITVOERBAAR

Merk op dat het in dit voorbeeld niet altijd gegarandeerd is dat tegen de tijd dat onze controle bereikt t.getState (), zal het nog steeds in de UITVOERBAAR staat.

Het kan gebeuren dat het onmiddellijk werd gepland door de Thread-Scheduler en kan de uitvoering voltooien. In dergelijke gevallen kunnen we een andere output krijgen.

3.3. Geblokkeerd

Een rode draad zit in de GEBLOKKEERD geef aan wanneer het momenteel niet geschikt is om te worden uitgevoerd. Het komt in deze toestand wanneer het wacht op een monitorvergrendeling en probeert toegang te krijgen tot een gedeelte van de code dat is vergrendeld door een andere thread.

Laten we proberen deze toestand te reproduceren:

openbare klasse BlockedState {openbare statische leegte hoofd (String [] args) gooit InterruptedException {Thread t1 = nieuwe Thread (nieuwe DemoThreadB ()); Thread t2 = nieuwe Thread (new DemoThreadB ()); t1.start (); t2.start (); Thread.sleep (1000); Log.info (t2.getState ()); System.exit (0); }} klasse DemoThreadB implementeert Runnable {@Override public void run () {commonResource (); } public static gesynchroniseerd void commonResource () {while (true) {// Oneindige lus om zware verwerking na te bootsen // 't1' verlaat deze methode niet // wanneer 't2' dit probeert in te voeren}}}

In deze code:

  1. We hebben twee verschillende threads gemaakt - t1 en t2
  2. t1 start en gaat het gesynchroniseerde commonResource () methode; dit betekent dat slechts één thread er toegang toe heeft; alle andere volgende threads die proberen toegang te krijgen tot deze methode, worden geblokkeerd voor verdere uitvoering totdat de huidige de verwerking heeft voltooid
  3. Wanneer t1 deze methode invoert, wordt deze in een oneindige while-lus bewaard; dit is alleen om de zware verwerking te imiteren, zodat alle andere threads deze methode niet kunnen binnengaan
  4. Nu wanneer we beginnen t2, het probeert het commonResource () methode, die al wordt gebruikt door t1, dus, t2 zal worden bewaard in de GEBLOKKEERD staat

Omdat we in deze staat zijn, bellen we t2.getState () en krijg de uitvoer als:

GEBLOKKEERD

3.4. Aan het wachten

Er zit een draad in AAN HET WACHTEN staat wanneer het wacht op een andere thread om een ​​bepaalde actie uit te voeren. Volgens JavaDocs kan elke thread deze status binnengaan door een van de volgende drie methoden aan te roepen:

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

Merk op dat in wacht() en toetreden () - we definiëren geen time-outperiode, aangezien dat scenario in de volgende sectie wordt behandeld.

We hebben een aparte tutorial waarin het gebruik van wacht(), melden () en informerenAll ().

Laten we voorlopig proberen deze staat te reproduceren:

openbare klasse WaitingState implementeert Runnable {openbare statische Thread t1; public static void main (String [] args) {t1 = nieuwe Thread (nieuwe WaitingState ()); t1.start (); } public void run () {Thread t2 = nieuwe Thread (nieuwe DemoThreadWS ()); t2.start (); probeer {t2.join (); } catch (InterruptedException e) {Thread.currentThread (). interrupt (); Log.error ("Thread onderbroken", e); }}} klasse DemoThreadWS implementeert Runnable {public void run () {probeer {Thread.sleep (1000); } catch (InterruptedException e) {Thread.currentThread (). interrupt (); Log.error ("Thread onderbroken", e); } Log.info (WaitingState.t1.getState ()); }}

Laten we bespreken wat we hier doen:

  1. We hebben het t1
  2. t1 creëert een t2 en begint het
  3. Terwijl de verwerking van t2 gaat verder, bellen we t2.join (), dit zet t1 in AAN HET WACHTEN staat tot t2 is klaar met de uitvoering
  4. Sinds t1 wacht op t2 om te voltooien, bellen we t1.getState () van t2

De output hier is, zoals je zou verwachten:

AAN HET WACHTEN

3.5. Getimed wachten

Er zit een draad in TIMED_WAITING staat wanneer het wacht op een andere thread om een ​​bepaalde actie uit te voeren binnen een bepaalde tijd.

Volgens JavaDocs zijn er vijf manieren om een ​​thread op te zetten TIMED_WAITING staat:

  1. thread.sleep (lange millis)
  2. wacht (int time-out) of wacht (int time-out, int nanos)
  3. thread.join (long millis)
  4. LockSupport.parkNanos
  5. LockSupport.parkUntil

Om meer te lezen over de verschillen tussen wacht() en slaap() in Java, bekijk hier dit speciale artikel.

Laten we voorlopig proberen om deze toestand snel te reproduceren:

openbare klasse TimedWaitingState {openbare statische leegte hoofd (String [] args) gooit InterruptedException {DemoThread obj1 = nieuwe DemoThread (); Thread t1 = nieuwe Thread (obj1); t1.start (); // De volgende slaap geeft ThreadScheduler genoeg tijd // om de verwerking van thread t1 Thread.sleep (1000) te starten; Log.info (t1.getState ()); }} klasse DemoThread implementeert Runnable {@Override public void run () {probeer {Thread.sleep (5000); } catch (InterruptedException e) {Thread.currentThread (). interrupt (); Log.error ("Thread onderbroken", e); }}}

Hier hebben we een thread gemaakt en gestart t1 die in de slaapstand wordt gebracht met een time-outperiode van 5 seconden; de output zal zijn:

TIMED_WAITING

3.6. Beëindigd

Dit is de toestand van een dode draad. Het is in de BEËINDIGD aangeven wanneer de uitvoering is voltooid of abnormaal is beëindigd.

We hebben een speciaal artikel dat verschillende manieren bespreekt om de draad te stoppen.

Laten we proberen deze toestand te bereiken in het volgende voorbeeld:

public class TerminatedState implementeert Runnable {public static void main (String [] args) gooit InterruptedException {Thread t1 = new Thread (new TerminatedState ()); t1.start (); // De volgende slaapmethode geeft // thread t1 genoeg tijd om Thread.sleep (1000) te voltooien; Log.info (t1.getState ()); } @Override public void run () {// Geen verwerking in dit blok}}

Hier, terwijl we met draad zijn begonnen t1, de allereerste verklaring Draad.sleep (1000) geeft genoeg tijd voor t1 om te voltooien en dus geeft dit programma ons de output als:

BEËINDIGD

Naast de threadstatus kunnen we de is levend() methode om te bepalen of de thread leeft of niet. Als we bijvoorbeeld de is levend() methode op deze thread:

Assert.assertFalse (t1.isAlive ());

Het keert terug false. Simpel gezegd, een draad leeft als en slechts als dat zo is is begonnen en is nog niet overleden.

4. Conclusie

In deze tutorial hebben we geleerd over de levenscyclus van een thread in Java. We hebben gekeken naar alle zes staten gedefinieerd door Thread.State opsomming en reproduceerde ze met snelle voorbeelden.

Hoewel de codefragmenten op bijna elke machine dezelfde uitvoer zullen geven, kunnen we in sommige uitzonderlijke gevallen een aantal verschillende uitvoer krijgen omdat het exacte gedrag van Thread Scheduler niet kan worden bepaald.

En zoals altijd zijn de codefragmenten die hier worden gebruikt, beschikbaar op GitHub.