Wątki

Program (proces) zawiera jeden lub więcej wątków. Każdy z wątków jest niezależną ścieżką działania programu.

Program jest wykonywany jeśli scheduler przydziela procesor jednemu z jego wątków.

Wątki działają w pełni niezależnie, ale mogą synchronizować działanie ze sobą.

Wątek zawiera:

·      kopię rejestrów procesora

·      własny stos

Wątki dzielą zasoby procesu:

·      otwarte pliki

·      dane statyczne

·      dane na stercie

·        W języku Java wątki są są obiektami dziedziczącymi po klasie java.lang.Thread. Zazwyczaj klasa działająca jako wątek dziedziczy po klasie Thread i przedefiniowuje metodę run().

·        Aby uruchomić wątek, należy wywołać metodę start().

Pierwszy program

Przetestuj

public class TestThreads {

  static int sharedVariable=0;

  public static void main(String[] args) {

    new Inc().start();

    new Dec().start();

  }

}

 

class Inc extends Thread

{

  public void run()

  {

    for(;;){

      TestThreads.sharedVariable++;

      System.out.println(TestThreads.sharedVariable);

    }

  }

}

 

class Dec extends Thread

{

  public void run()

  {

    for(;;){

      TestThreads.sharedVariable--;

      System.out.println(TestThreads.sharedVariable);

    }

  }

}

Usypianie wątków

Aby zawiesić wykonanie wątku należy wywołać metodę sleep(). Metoda może wyrzucać wyjątek InterruptedException, dlatego należy go przechwytać

try{sleep(1000);}

catch (InterruptedException e){}

 

Dodaj opóźnienie do klas Inc i Dec.

Zatrzymywanie wątków

Aby zatrzymać wątek, najlepiej jest zadeklarować zmienną boolean testowaną w pętli i ustawiać ją z zewnątrz. Dodaj zmienną boolean stop do klas Inc i Dec. Przerwij petlę, jeżeli stop ma wartość true.

Wątek funkcji main

Każda metoda jest wykonywana przez jakiś wątek. Jest on zwracany przez statyczną metodę Thread.currentThread();

Przetestuj poniższy kod:

public class TestThreads {

  static int sharedVariable=0;

  public static void main(String[] args) {

    Inc inc = new Inc();

    inc.start();

    Dec dec=new Dec();

    dec.start();

    try{Thread.currentThread().sleep(10*1000);}

    catch (InterruptedException e){}

    inc.stop=true;

    dec.stop=true;

    try{Thread.currentThread().sleep(5*1000);}

    catch (InterruptedException e){}

    System.out.println("main finished");

  }

}

 

Problem producenta i konsumenta

Producent – produkuje dane i umieszcza je w buforze

Konsument – odczytuje dane z bufora i konsumuje je

Bufor ma ograniczoną pojemność

Stwórz nowy projekt i dodaj do niego klasy Buffer, Producer, Consumer oraz TestProducerConsumer.

Kod Buffer.java

public class Buffer {

  static final int BUF_SIZE = 8;

  int[] buf = new int[BUF_SIZE];

  int count=0;

 

  boolean put(int element)

  {

    if(count==BUF_SIZE)return false;

    buf[count]=element;

      //[wywłaszczenie??]

    count++;

    return true;

   }

  

   int get()

   {

     if(count==0){

       // co wtedy?

       return -1;

     }

     int element = buf[0];

     for(int i=0;i<count-1;i++)buf[i]=buf[i+1];

      //[wywłaszczenie??]

 

     count--;

     return element;

    }

}

 

 

 

Kod Producer.java

public class Producer extends Thread {

  Buffer buf;

  int sleepTime=600;

  boolean stop=false;

 

  public void run()

  {

    for(int i=0;!stop;){

        try{

          boolean result = buf.put(i);

          if(result){

            System.out.println("Producer send:"+i);         

            i++;

          }

          sleep(sleepTime);

        }

        catch (InterruptedException e){System.err.println(e);}

    }

  }

}

 

Kod Consumer.java

public class Consumer extends Thread {

  Buffer buf;

  int sleepTime=300;

  boolean stop=false;

 

  public void run()

  {

    for(;!stop;){

        try{

          int result = buf.get();

          if(result>=0){

            System.out.println("Consumer get:"+result);         

          }

          sleep(sleepTime);

        }

        catch (InterruptedException e){System.err.println(e);}

    }

  }

}

 

Kod TestProducerConsumer.java

public class TestProducerConsumer {

 

  public static void main(String[] args) {

    Buffer buf = new Buffer();

   

    Producer p = new Producer();

    p.buf = buf;

    p.start();

   

    Consumer c = new Consumer();

    c.buf = buf;

    c.start();

 

    try{

        Thread.currentThread().sleep(20 * 1000);

        p.stop=true;

        c.stop=true;

    }

    catch(InterruptedException e){}

    System.out.println("main finished");

   

  }

}

 

Do wykonania

Przeprowadź eksperymenty różnymi czasami sleepTime wątków Producer i Consumer. Niech Producer szybciej produkuje dane niż Consumer je odbiera!

Co się stanie jeżeli wątki Producer lub Consumer zostaną zawieszone w miejscach oznaczonych jako [wywłaszczenie]?

Monitory

Monitor jest mechanizmem zapewniającym wzajemne wykluczanie i synchronizację wątków. Wątek wykonujący sekcję kodu oznaczoną jako synchronized lub metodę zadeklarowaną jako synchronized staje się właścicielem monitora związanego z obiektem.

·        Wątek wykonujący metodę Buffer.put() lub Buffer.get()powinien zablokować dostęp do bufora na czas modyfikacji danych. Dlatego metody te należy zadeklarować jako synchroniczne.
synchronized boolean put(int element) {...}
synchronized int get(){...}

·        Nawet jeżeli Producer wykonujący metodę put() zostanie zawieszony, to Consumer nie będzie mógł wykonać metody get(). Zostanie on zawieszony i jego referencja zostanie umieszczona w kolejce wątków oczekujących na przejęcie monitora.

Do wykonania

Zmień metody put i get bufora na metody synchroniczne

 

Klasa Displayer

Dodajemy klasę, która będzie wyświetlała zawartość bufora.

Poniższy kod niepublicznej klasy można np.: dołączyć do TestProducerConsumer.java.

class Displayer extends Thread

{

  Buffer buf;

  boolean stop=false;

  public void run()

  {

    for(;!stop;){

      synchronized (buf) {

        System.out.print("[ ");

        for (int i = 0; i < buf.count; i++) System.out.print(buf.buf[i] + " ");

        System.out.println(" ]");

      }

      try {

          sleep(100);

      }

      catch (Exception e) {}

    }

  }

 }

 

Należy:

·         uruchomić i zatrzymać wątek Displayer w funkcji main()

·         poeksperymentować z różnymi czasami sleepTime wątków Producer i Consumer.

Metody wait i notify monitora

Metody te służą do synchronizacji wątków za pośrednictwem obiektu zawierającego monitor.

wait – zwalnia monitor, dodaje referencję wątku do kolejki monitora i zawiesza go

notify – rekatywuje wątek

Bufor występujący w problemie producenta-konsumenta powinien zawieszać wątek Consumer, kiedy bufor jest pusty. Jeżeli konsument jest zawieszony, powinien zostać reaktywowany w momencie, kiedy element zostanie dodany do bufora.

 

·        Umieść wywołanie wait() w metodzie get monitora zamiast zwracać –1.

     if(count==0){

       // co wtedy?

       return -1;

     }

Zamień if na while

·        Umieść wywołanie notify() w metodzie put monitora po dodaniu elementu do bufora.

·