19.3. Programszál életciklusa

A következő ábra bemutatja a szál különböző állapotait, amibe élete során tud kerülni, és illusztrálja, hogy melyik metódusok meghívása okozza az átmenetet egy másik fázisba. Az ábra nem egy teljes állapotdiagram, inkább csak egy áttekintés a szál életének fontosabb és egyszerűbb oldalairól. A továbbiakban is a Clock példát használjuk, hogy bemutassuk a szál életciklusát különböző szakaszaiban.

Programszál életciklusa

Programszál létrehozása

Az alkalmazás, melyben egy szál fut, meghívja a szál start metódusát, amikor a felhasználó a programot elindítja. A Clock osztály létrehozza a clockThread szálat a start metódusában, ahogy a lenti programkód bemutatja:

public void start() {
    if (clockThread == null) {
        clockThread = new Thread(this, "Clock");
        clockThread.start();
    }
}

Miután a kiemelt utasítás végrehajtódik, a clockThread az „új szál” (New Thread) állapotban van. Egy szál ebben az állapotában csupán egy üres szál, semmiféle rendszererőforrás nincs hozzárendelve. Ebben az állapotban csak elindítható a szál. A start metóduson kívül bármilyen metódus meghívása értelmetlen, és IllegalThreadStateException kivételt eredményez. (Alapértelmezetten a rendszer mindig ezt a hibaüzenetet dobja, ha egy olyan metódus kerül meghívásra, amelyik a szál pillanatnyi állapotában nem engedélyezett.)

Érdemes megjegyezni, hogy a Clock típusú példány az első paraméter a szál konstruktorban. Ez a paraméter meg kell, hogy valósítsa a Runnable interfészt, csak így lehet a konstruktor paramétere. A Clock szál a run metódust a Runnable interfésztől örökli. A konstruktor második paramétere csupán a szál neve.

19.3.1 Programszál nem futtatható állapotba állítása

Egy szál akkor válik nem futtathatóvá, ha a lenti események közül bármelyik is bekövetkezik:

  • Meghívásra kerül a sleep metódusa.
  • A szál meghívja a wait metódust, hogy az várjon egy bizonyos feltétel teljesülésére.
  • A szál blokkolt egy I/O művelet miatt.

A clockThread a Clock szálon belül akkor kerül nem futtatható állapotba, mikor a futás metódus meghívja a sleep-et a jelenlegi szálban:

public void run() {
    Thread myThread = Thread.currentThread();
    while (clockThread == myThread) {
        repaint();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
           // A VM nem akarja, hogy tovább aludjunk,
           // szóval irány vissza dolgozni 
        }
    }
}

Az alatt a másodperc alatt, amíg a clockThread alvó állapotban van, a szál nem fut, még akkor sem fog, ha a processzor erőforrásai elérhetővé válnak. Miután letelik az egy másodperc, a szál újra futtathatóvá válik; ha a processzor erőforrásai elérhetővé válnak, a szál újra futni kezd.

Minden egyes nem futtatható állapotba kerüléskor, egy specifikus és megkülönböztethető exit metódus téríti vissza a szálat a futtatható állapotba. Egy exit hívás után csak a megfelelő feltételek teljesülése esetén fog ismét futni. Például: miután egy szál alvó állapotba lett helyezve, a meghatározott ezredmásodpercek lejárta után újból futtatható állapotba kerül. A következő felsorolás leírja a nem futtatható állapot kilépési feltételeit:

  • Ha a szál alvó állapotba lett helyezve, a meghatározott időnek le kell telnie.
  • Ha egy szál egy feltételre vár, akkor egy másik objektumnak kell értesítenie a várakozó szálat a feltétel teljesüléséről a notify vagy notifyAll metódus meghívásával.
  • Ha egy szál blokkolva volt egy I/O művelet miatt, az I/O műveletnek be kell fejeződnie.

19.3.2 Programszál leállítása

Habár a Thread osztályban rendelkezésre áll a stop metódus, ezen metódus alkalmazása nem javallott, mert nem biztonságos, pl. adatvesztés léphet fel. Egy szálnak inkább saját magának kell befejezni a futását úgy, hogy a run metódusa természetesen áll le. Például a while ciklus a run metódusban egy véges ciklus 100-szor fut le, majd kilép:

public void run() {
    int i = 0;
    while (i < 100) {
        i++;
        System.out.println("i = " + i);
    }
}

A szál a run metódusban természetesen „hal meg” mikor a ciklus befejeződik és a run metódus kilép.
Nézzük meg, hogyan hajtja végre a Clock szál a saját halálát. Nézzük meg újra a Clock run metódusát:

public void run() {
    Thread myThread = Thread.currentThread();
    while (clockThread == myThread) {
        repaint();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            //the VM doesn't want us to sleep anymore,
            //so get back to work
        }
    }
}

A kilépési feltétel ebben a run metódusban ugyanaz a kilépési feltétele, mint a while ciklusnak, mert a ciklus után nincs kód:

while (clockThread == myThread) {

E feltétel jelzi, hogy a ciklus kilép, ha a jelenleg futásban lévő szál nem egyezik a clockThread-el. Mikor lesz ez a helyzet? Akkor, amikor a felhasználó elhagyja az oldalt, az alkalmazás, melyben a szál fut, meghívja a szál stop metódusát. Ez a metódus ezután beállítja a clockThread-et null értékre, így utasítva a fő ciklust, hogy szakítsa meg a futás metódust:

public void stop() {    // applets' stop method
    clockThread = null;
}

19.3.3 Programszál státusz tesztelése

Az 5.0-ás verzióban került bevezetésre a Thread.getState metódus. Mikor ez kerül meghívásra, az alábbi Thread.State értékek valamelyike tér vissza:

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED

A Thread osztályhoz tartozó API tartalmazza az isAlive metódust is. Az isAlive metódus igaz visszatérési értéket generál, ha a szálat elindították, de még nem lett leállítva. Ha az isAlive visszatérési értéke hamis, akkor tudhatjuk, hogy a szál vagy új szál (NEW), vagy halott állapotban van (TERMINATED). Ha a visszatérési érték igaz, akkor viszont a szál vagy futtatható (RUNNABLE), vagy nem futtatható állapotban van.

Az 5.0-ás verziót megelőzően nem lehetett különbséget tenni az új szál vagy a halott szálak között. Ugyancsak nem lehetett megkülönböztetni a futtatható, vagy nem futtatható állapotban lévő száltól.

A processzor használatának feladása

Ahogyan el tudjuk képzelni, CPU-igényes kódok negatív hatással vannak más szálakra, amelyek azonos programban futnak. Vagyis próbáljunk jól működő szálakat írni, amelyek bizonyos időközönként önkéntesen felhagynak a processzor használatával, ezzel megadva a lehetőséget minden szálnak a futásra.
Egy szál önkéntesen abbahagyhatja a CPU használatát a yield metódus meghívásával.