ESWE1P1 Opdracht 3: Multi-threaded programmeren in Java.

© Harry Broeders.

Java.

In deze practicum opdracht maak je kennis met Java. We zullen hetzelfde producer/consumer programma uit opgave 2 als voorbeeld gebruiken. Ook zullen we de scheduling eigenschappen van Java bestuderen.

Ontwikkelomgeving.

De gratis ontwikkelomgeving voor Java van Sun is in lokaal 428 zowel in de WindowsXP als in de Linux installatie opgenomen.

Er zijn diverse andere (gratis) ontwikkelomgevingingen beschikbaar:

Het is handig om ook de documentatie en tutorial bij de hand te hebben in 428:

Opdracht 3a.

Installeer een Java ontwikkelomgeving  naar keuze en test deze door een eenvoudig "Hello world" programma te schrijven.

Voorbeeld programma in Java.

We gebruiken in deze opgave hetzelfde voorbeeld als in opgave 2.

De class BoundedBuffer is in de file BoundedBuffer.java als volgt gedefinieerd:

public class BoundedBuffer {
   private char buffer[];
   private int indexPut;
   private int indexGet;
   private int numberInBuffer=0;
   private int size;

   public BoundedBuffer(int length) {
      size=length;
      buffer=new char[size];
      indexPut=0;
      indexGet=0;
   }
   public synchronized void put(char c) throws InterruptedException {
      while (numberInBuffer==size)
         wait();
      buffer[indexPut++]=c;
      indexPut%=size;
      numberInBuffer++;
      notify();
   }
   public synchronized char get() throws InterruptedException {
      while (numberInBuffer==0)
         wait();
      char c=buffer[indexGet++];
      indexGet%=size;
      numberInBuffer--;
      notify();
      return c;
   }
}

De links in het programma verwijzen naar de online API documentatie van Sun. Met behulp van de method put kan een karakter in de BoundedBuffer worden geschreven en met behulp van de method get kan een karakter uit de BoundedBuffer worden gelezen.

De class Consumer is in de file Consumer.java gedefinieerd:

public class Consumer implements Runnable {
   public Consumer(BoundedBuffer bb) {
      b=bb;
   }
   public void run() {
      try {
         synchronized (System.out) {
            System.out.println("Thread: "+Thread.currentThread().getName()+" gestart");
         }
         for (int i=0; i<2000; ++i) {
            char c=b.get();
            synchronized (System.out) {
               System.out.print(c);
            }
         }
         synchronized (System.out) {
            System.out.println("Thread: "+Thread.currentThread().getName()+" gestopt");
         }
      }
      catch(InterruptedException e) {
         System.err.println("Thread: "+Thread.currentThread().getName()+" interrupted");
      }
   }
   private BoundedBuffer b;
}

Er worden 2000 karakters uit de buffer gelezen met behulp van de method get. Elk karakter wordt met de method print op het scherm (System.out) gezet. Ook wordt het starten en stoppen van de thread gemeld.

De class Producer moet in de file Producer.java gedefinieerd worden:

public class Producer implements Runnable {
/* Implementeer hier de Producer
   Deze producer moet 1000 x het karakter dat als eerste argument aan de
   constructor wordt meegegeven in de buffer plaatsen dat als tweede
   argument aan de constructor wordt meegegeven. */
}

In de file TestBoundedBuffer.java is het test programma gedefinieerd:

public class TestBoundedBuffer {
   public static void main(String[] args) {
      if (args.length!=3) {
         System.err.println("Usage: java TestBoundedBuffer <Priority Consumer> "+
                            "<Priority Frikadel Producer> <Priority Kroket Producer>");
         System.err.println("Minimum Priority = "+Thread.MIN_PRIORITY);
         System.err.println("Maximum Priority = "+Thread.MAX_PRIORITY);
         System.err.println("Normal Priority = "+Thread.NORM_PRIORITY);
      }
      else {
         BoundedBuffer b=new BoundedBuffer(16);
         char frikadel='F';
         char kroket='K';
         Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
         Thread tc =new Thread(new Consumer(b));
         Thread tp1=new Thread(new Producer(frikadel, b));
         Thread tp2=new Thread(new Producer(kroket, b));
         tc.setPriority(Integer.parseInt(args[0]));
         tp1.setPriority(Integer.parseInt(args[1]));
         tp2.setPriority(Integer.parseInt(args[2]));
         System.out.println("Output voor Consumer priority = "+tc.getPriority()+
                            " Frikadel Producer priority = "+tp1.getPriority()+
                            " Kroket Producer priority = "+tp2.getPriority());
         tc.start();
         tp1.start();
         tp2.start();
      }
   }
}

In de method main krijgt de main thread een hoge prioriteit. De main thread moet de hoogste prioriteit hebben omdat deze thread de andere threads start en we willen de onderlinge wisselwerking van deze threads bestuderen.

In dit programma "bakt" de ene producer frikadellen (weergegeven met de letter F) en "bakt" de andere producer kroketten (weergegeven met de letter K). De consumer "eet" de frikadellen en kroketten op.

Bij het starten van het programma moeten op de command line 3 parameters worden meegegeven. Dit zijn achtereenvolgens de prioriteit van de consumer, de prioriteit van de frikadellen producer en de prioriteit van de kroketten producer.

De main thread hoeft in Java niet te wachten totdat de andere threads geëindigd zijn. Het proces wordt pas beëindigen als alle threads zijn beëindigd.

Opdracht 3b.

Implementeer de class Producer en run TestBoundedBuffer met de volgende prioriteiten: consumer = 1, frikadellen producer = 2 en kroketten producer = 3.

java TestBoundedBuffer 1 2 3

Verklaar de uitvoer! Is de uitvoer deterministisch? Is Java bruikbaar als Real-Time programmeertaal?

Opdracht 3c.

Een van de grote voordelen van Java is dat het platform onafhankelijk is. Test dit door de .class files naar een ander platform (QNX) te kopiëren en het programma daar te starten. Bijvoorbeeld op QNX:

j9 TestBoundedBuffer 1 2 3

Is de uitvoer op beide platformen gelijk? Is de uitvoer op QNX wel deteministisch? Je kunt op QNX gebruik maken van de IDE Momentics om Java applicaties te ontwikkelen. Zie Launch>Momentics. Zolang we nog geen nieuwe license hebben kun je j9c gebruiken als java compiler en j9 als java virtual machine.

Opdracht 3d.

Vervang in de code van de class BoundedBuffer de aanroep notify() in notifyAll(). Run het programma met de volgende prioriteiten: consumer = 1, frikadellen producer = 2 en kroketten producer = 3.

java TestBoundedBuffer 1 2 3

Verklaar de verschillen in de uitvoer vergeleken met opgave 3b en opgave 3c.

Test ook de volgende prioriteiten:

java TestBoundedBuffer 3 2 1

Verklaar de uitvoer! Is de uitvoer deterministisch?

Verder met opdracht 4...