Kapitel 17. Flertrådet programmering

Indhold:

Kapitlet forudsættes ikke i resten af bogen.

Forudsætter Kapitel 12, Interfaces (Kapitel 16, Netværkskommunikation og Kapitel 10, Appletter og grafik bruges i nogle eksempler).

Når man kommer ud over den grundlæggende programmering, ønsker man tit at lave programmer, som udfører flere opgaver løbende. Det kan f.eks. være et tekstbehandlingsprogram, hvor man ønsker at gemme eller sende en udskrift til printeren, mens man redigerer videre, eller man ønsker løbende stavekontrol samtidig med, at man skriver. Skrivningen må ikke blive forsinket af, at programmet sideløbende forbereder en udskrift eller kontrollerer stavningen. Disse delprocesser (også kaldet tråde) har lav prioritet i forhold til at håndtere brugerens input og vise det på skærmen, og selvom de midlertidigt skulle gå i stå, skal de andre dele af programmet køre videre.

Et flertrådet program er et program med flere tilsyneladende samtidige programudførelsespunkter (i virkeligheden vil CPU'en skifte meget hurtigt rundt mellem punkterne og udføre lidt af hver).

17.1. Princip

Det er ret let at programmere flertrådet i Java. Man opretter en ny tråd med et objekt i konstruktøren:

  Thread tråd;
  tråd = new Thread(obj);

Objektet obj skal implementere Runnable-interfacet, f.eks.:

public class UdførbartObjekt implements Runnable
{
  public void run()  // kræves af Runnable
  {
    // her starter den nye tråd med at køre
    // ...
  }
}

Tråden er nu klar til at udføre run()-metoden på objektet, men den er ikke startet endnu. Man starter tråden ved at kalde start()-metoden på tråd-objektet:

  tråd.start();
  // her kører den oprindelige tråd videre, mens den nye kører i run()
  // ...

Derefter vil der være to programudførelsespunkter: Et vil være i koden efter kaldet til start(), og den anden vil være ved begyndelsen af run()-metoden.

Figur 17-1. Java

En tråd oprettes med et objekt, der implementerer Runnable-interfacet. Når start() kaldes på tråden, vil den begynde at udføre run() på objektet.

17.1.1. Eksempel

public class SnakkesagligPerson implements Runnable
{
  private String navn;
  private int ventetid;

  public SnakkesagligPerson(String n, int t)
  {
    navn = n;
    ventetid = t;
  }

  public void run()
  {
    for (int i=0; i<5; i++)
    {
      System.out.println(navn+": bla bla bla "+i);
      try {  Thread.sleep(ventetid); } catch (Exception e) {} // vent lidt
    }
  }
}

Objekter af typen SnakkesaligPerson kan køre i en tråd (implements Runnable).

I konstruktøren angives navnet på personen og hvor lang tid, der går, mellem hver gang personen taler.

Når run() udføres, skriver den personens navn + bla bla bla ud så ofte som angivet.

Da Thread.sleep() kan kaste undtagelser af typen InterruptedException, er vi nødt til at indkapsle koden i en try-catch-blok (disse undtagelser forekommer aldrig i praksis).

Vi kan nu oprette en snakkesalig person, der siger noget hvert sekund:

    SnakkesagligPerson p = new SnakkesagligPerson("Brian",1000);

... og en tråd, der er klar til at udføre p.run() og lade personen snakke:

    Thread t = new Thread(p);

... og til sidst startes tråden, så personen snakker:

    t.start();

Her ses et samlet eksempel, der opretter 3 snakkesalige personer, Jacob, Troels og Henrik, og lader dem snakke i munden på hinanden (i hver sin tråd).

public class BenytSnakkesagligePersoner
{
  public static void main(String arg[])
  {
    SnakkesagligPerson p = new SnakkesagligPerson("Jacob",150);
    Thread t = new Thread(p); // Ny tråd, klar til at udføre p.run()
    t.start(); // .. Nu starter personen med at snakke...

    p = new SnakkesagligPerson("Troels",400);
    t = new Thread(p);
    t.start();

    // Det kan også gøres meget kompakt:
    new Thread(new SnakkesagligPerson("Henrik",200)).start();
  }
}

Resultatet bliver:

Jacob: bla bla bla 0
Troels: bla bla bla 0
Henrik: bla bla bla 0
Jacob: bla bla bla 1
Henrik: bla bla bla 1
Jacob: bla bla bla 2
Troels: bla bla bla 1
Henrik: bla bla bla 2
Jacob: bla bla bla 3
Henrik: bla bla bla 3
Jacob: bla bla bla 4
Troels: bla bla bla 2
Henrik: bla bla bla 4
Troels: bla bla bla 3
Troels: bla bla bla 4

Bemærk, at udførelsen af main(), der faktisk sker i en fjerde tråd, afsluttes med det samme, men at programmet kører videre, indtil de tre tråde er færdige med deres opgaver; Java fortsætter med at udføre et program, så længe der er tråde, der stadig er aktive, dvs. ikke har returneret fra run().