19.2. Szálak példányosítása

Ha a fenti megközelítés nem alkalmas a feladat ellátására, akkor saját szál példányosítása lehet a megoldás. Ez a fejezet elmagyarázza, hogyan lehet a szál run metódusát egyénivé alakítani.

A run metódusban van, amit a szál ténylegesen csinál, ennek a kódja határozza meg a futást. A szál run metódussal mindent meg lehet tenni, amit a Java programnyelvben le lehet írni. Pl.: prímszámokkal való számolás, rendezés, animáció megjelenítés.

A Thread osztály példányosításával egy új szál jön létre, ami alapból nem csinál semmit, de lehetőség van a leszármazott osztályban tartalommal megtölteni.

19.2.1 Thread leszármazott és a run felülírása

Az első lépés a szálak testreszabásánál, hogy Thread osztály leszármazottját létrehozzuk, és az üres run metódusát felülírjuk, hogy csináljon is valamit. Nézzük meg a SimpleThread osztályt, amely ezt teszi:

public class SimpleThread extends Thread {
    public SimpleThread(String str) {
        super(str);
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i + " " + getName());
            try {
                sleep((long)(Math.random() * 1000));
            } catch (InterruptedException e) {}
        }
        System.out.println("KÉSZ! " + getName());
    }
}

A SimpleThread osztály első metódusa egy konstruktor, amely paraméterként egy String-et fogad. A konstruktor meghívja az ősosztály konstruktorát, ami azért érdekes számunkra, mert az beállítja a szál nevét. Ezt a nevet használjuk a későbbi programban a szál felhasználói azonosítására. A nevet később a getName metódussal tudjuk lekérdezni.

A SimpleThread osztály következő metódusa a run. Itt történik a Thread érdemi működése. Ebben az esetben egy tizes for ciklust tartalmaz. Minden ciklusban kiírja az iteráció számát, a Thread nevét, és utána altatásra kerül véletlenszerűen 1 másodperces határon belül. Ha végzett, a KÉSZ! feliratot írja ki a szál nevével együtt. Ennyi a SimpleThread osztály. Használjuk fel ezt a TwoThreadsTest-ben.

A TwoThreadsTest osztály main metódusába két SimpleThread szálat hozunk létre: Jamaica és Fuji. (Ha valaki nem tudná eldönteni hova menjen nyaralni, akkor használja ezt a programot.)

public class TwoThreadsTest {
    public static void main (String[] args) {
        new SimpleThread("Jamaica").start();
        new SimpleThread("Fiji").start();
    }
}

A main metódus azonnal mindkét szálat létrehozza a start metódus meghívásával, amely a run metódust hívja meg. Fordítás és futtatás után kibontakozik az úti cél. A kimenet hasonló kell, hogy legyen:

Two Thread Test

Figyeljük meg, hogy a szálak kimenete mennyire független egymástól. Ennek az az oka, hogy a SimpleThread szálak egyidejűleg futnak. Mindkét run metódus fut, és mindkettőnek van saját kimenete ugyanakkor. Ha a ciklus befejeződik, a szálak leállnak és meghalnak.

19.2.2 Runnable interfész példányosítása

A következő Clock applet a jelenlegi időt mutatja, és azt másodpercenként frissíti. A frissítés állandó, mivel az óra kijelzése egy saját szálon fut.

Az applet más módszert használ, mint a SimpleThread. Itt ugyanis a szülőosztály csak az Applet lehet, de így az egyszeres öröklődés miatt a Thread már nem lehet szülő. Ezért a példa Thread leszármazott létrehozása helyett egy Runnable interfészt implementál, ezért a run metódust ebben definiálja. A Clock létrehoz egy szálat, amelynek a frissítés a célja.

import java.awt.*;
import java.util.*;
import java.applet.*;
import java.text.*;
public class Clock extends java.applet.Applet implements Runnable {
    private volatile Thread clockThread = null;
    DateFormat formatter;     // Formats the date displayed
    String lastdate;          // String to hold date displayed
    Date currentDate;         // Used to get date to display
    Color numberColor;        // Color of numbers
    Font clockFaceFont;
    Locale locale;
    public void init() {
        setBackground(Color.white);
        numberColor = Color.red;
        locale = Locale.getDefault();
        formatter =
            DateFormat.getDateTimeInstance(DateFormat.FULL,
            DateFormat.MEDIUM, locale);
        currentDate = new Date();
        lastdate = formatter.format(currentDate);
        clockFaceFont = new Font("Sans-Serif",
                                 Font.PLAIN, 14);
        resize(275,25);
   }
    public void start() {
        if (clockThread == null) {
            clockThread = new Thread(this, "Clock");
            clockThread.start();
        }
    }
    public void run() {
        Thread myThread = Thread.currentThread();
        while (clockThread == myThread) {
            repaint();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e){ }
        }
    }
    public void paint(Graphics g) {
        String today;
        currentDate = new Date();
        formatter =
             DateFormat.getDateTimeInstance(DateFormat.FULL,
             DateFormat.MEDIUM, locale);
        today = formatter.format(currentDate);
        g.setFont(clockFaceFont);
        // Erase and redraw 
        g.setColor(getBackground());
        g.drawString(lastdate, 0, 12);
        g.setColor(numberColor);
        g.drawString(today, 0, 12);
        lastdate = today;
        currentDate=null;
    }
    public void stop() {
        clockThread = null;
    }
}

A Clock applet run metódusa addig nem lép ki a ciklusból, amíg a böngésző le nem állítja. Minden ciklusban az óra kimenete újrarajzolódik. A paint metódus lekérdezi az időt, formázza és kijelzi azt.

Kell-e a Runnable interfész?

Két módszert ismertettünk a run metódus előállítására.

  • Leszármazott Thread osztály létrehozása és a run metódus felülírása.
  • Egy olyan osztály létrehozása, amelyik megvalósítja a Runnable interfészt és így a run metódust is. Ebben az esetbe a Runnable objektum szolgáltatja a run metódust a szálnak. Ezt feljebb láthatjuk.

Megjegyzés: A Thread is megvalósítja a Runnable interfészt.

Ahhoz, hogy egy böngészőben futhasson a Clock osztálynak az Applet osztály leszármazottjának kell lennie. Továbbá a Clock applet folyamatos kijelzése miatt egy szálra van szüksége, hogy ne a processzt vegye át, amiben fut. (Néhány böngésző figyelhet arra, hogy az appletek saját szálat kapjanak, elkerülendő, hogy a hibás appletek elvegyék a böngésző szálát. De erre nem építhetünk egy applet írásánál. Az általunk írt appletnek tudnia kell a saját szál létrehozását, ha olyan munkát végez.) De mivel a Java nyelv nem engedélyezi a több osztályokon át való öröklődést, a Clock nem lehet egyszerre a Thread és az Applet osztály leszármazottja. Így a Clock osztálynak egy Runnable interfészen keresztül kell a szálak viselkedését felvennie.