Advanced   Java   Services await() und signal()


wait() und notify() für Locks

Das Konzept wait() und notify() wurde komplett in das neue Lockkonzept aus dem Package concurrent übertragen und funktioniert sinngemäß genauso. Im Interface Lock angelegt ist die Methode newCondition() die ein Objekt vom Typ Condition liefert.


Das Interface   java.util.concurrent.locks.Condition

Das Interface enthält await() und signal()-Methoden, die das Pendant zu wait() und notify() bilden.

java.util.concurrent.locks.Condition
ReturntypName der Methode
voidawait()
Causes the current thread to wait until it is signalled or interrupted.
booleanawait(long time, TimeUnit unit)
Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses.
longawaitNanos(long nanosTimeout)
Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses.
voidawaitUninterruptibly()
Causes the current thread to wait until it is signalled.
booleanawaitUntil(Date deadline)
Causes the current thread to wait until it is signalled or interrupted, or the specified deadline elapses.
voidsignal()
Wakes up one waiting thread.
voidsignalAll()
Wakes up all waiting threads.

Die await() Methode bietet mehr Konfigurationsmöglichkeiten als die ältere wait()-Methode.


await() und signal() sind ein Team

Mit await() geht ein Thread in einen Wartezustand, der im Normalfall durch ein signal() oder signalAll() beendet wird. In Ausnahmefällen kann ein await() auch durch eine Interruptaufforderung beendet werden. await(), signal() und signalAll() sind im Unterschied zu wait() und notify() über Condition an den Lock gebunden. Sie sind keine Methoden der Klasse Object.


Was zu beachten ist

await() und signal() entsprechen zwar dem Paar wait() und notify(). Es muß jedoch etwas mehr beachtet werden.


Übertragung der wait() notify() Beispiele

Die folgenden Beispiele sind die Übertragung der Beispiele aus dem Kapitel wait() und notify():


Beispiel 1: Dreimal await, einmal signal

Der Thread

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;


public class Waiter extends Thread
{
  private Lock lock;
  private Condition condition;

  public Waiter(Lock lock, Condition condition)
  {
    this.lock = lock;
    this.condition = condition;
  }

  public void run()
  {
    try
    {
      lock.lock();
      System.out.println(this.getName() + " awaits");
      condition.await();
      System.out.println(this.getName() + " signalled");
    }
    catch(InterruptedException ex)
    {
      System.out.println(this.getName() + " interrupted");
    }
    finally
    {
      lock.unlock();
    }
  }
}

Die Mainklasse. Damit signal() und await() vom selben Lock geschützt werden, werden der Lock und die Condition in main angelegt und dem Thread übergeben.

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
*/
public class Await_Signal_01
{
  public static void main(String[] args)
  {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    Waiter w0 = new Waiter(lock, condition);
    Waiter w1 = new Waiter(lock, condition);
    Waiter w2 = new Waiter(lock, condition);
    w0.start();
    w1.start();
    w2.start();

    System.out.println("main goes to sleep");
    try{ TimeUnit.SECONDS.sleep(2); } catch(InterruptedException ex) {}

      System.out.println("main calls signal");
    lock.lock();
    condition.signal();
    lock.unlock();

    try{ TimeUnit.SECONDS.sleep(2); } catch(InterruptedException ex) {}

    w0.interrupt();
    w1.interrupt();
    w2.interrupt();

  }
}

Eine Ausgabe

Thread-2 awaits
Thread-1 awaits
main calls signal
Thread-0 signalled
Thread-1 interrupted
Thread-2 interrupted

Beispiel 2: Dreimal await, einmal signalAll

Der Waiter ist derselbe wie im ersten Beispiel

In der Mainklasse wird das sighnal() ersetzt durch signalAll(). Interrupt-Aufrufe sind dann nicht mehr noitwendig.

public class Await_Signal_01
{
   public static void main(String[] args)
   {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    Waiter w0 = new Waiter(lock, condition); //
    Waiter w1 = new Waiter(lock, condition); //
    Waiter w2 = new Waiter(lock, condition); //
    w0.start();
    w1.start();
    w2.start();

    try{ TimeUnit.SECONDS.sleep(2); } catch(InterruptedException ex) {}

      System.out.println("main calls signal");
    lock.lock();
    condition.signalAll();
    lock.unlock();
   }
}

Hier eine mögliche Ausgaben des Programms

Thread-0 awaits
Thread-1 awaits
Thread-2 awaits
main calls signal
Thread-0 signalled
Thread-1 signalled
Thread-2 signalled

Consumer-Producer Modelle

Da auch await() den Lock zurückgibt eignet es sich genauso für sogenante Consumer-Producer Modelle wie wait(). Consumer und Producer sind Threads die ein Lager leeren oder füllen, d.h. sie greifen auf ein gemeinsames Lager zu. Das Lager bietet put() und get() Methoden an mit denen man Waren im Lager deponieren oder entnehmen kann. put() und get() können mit wait() in einen Wartezustand gehen je nachdem ob das Lager voll oder leer ist. Daher sind put() und get() durch einen Lock geschützt.


Beispiel 3

Die Lösung mit Locks funktioniert nach dem gleichen Prinzip wie die Lösung bei wait() notify()

Das Lager

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Stock
{
   private int MAX;     // maximaler Bestand
   private int content; // aktueller Lagerbestand
   private int zugriff; // Zugriffszähler egal ob Producer oder Consumer
   private boolean available = false;

   private Lock lock = new ReentrantLock();
   private Condition condition = lock.newCondition();

   public Stock(int MAX)
   {
      this.MAX = MAX;
      this.content = MAX - 1;
      zugriff = 0;
   }

   public void put()
   {
      lock.lock();
      try
      {
         zugriff++;

         // solange was da, braucht nicht aufgefüllt werden, also warten
         while(available == true)
         {
            System.out.println(Thread.currentThread().getName() + " must wait");
            try
            {
               condition.await();
            }
            catch(InterruptedException ex)
            {
               // ex.printStackTrace();
            }
         }
         // hier ist available = false
         System.out.println(Thread.currentThread().getName() + " ends waiting");
         content++;
         // dadurch wird available true !
         System.out.println(Thread.currentThread().getName() + " puts 1 content = " + content);
         available = true;
         condition.signalAll();
      }
      finally
      {
         lock.unlock();
      }
   }

   public synchronized int get()
   {
      lock.lock();
      try
      {
         zugriff++;

         // solange nichts da ist, kann nichts abgeholt werden, also warten
         while(available == false)
         {
            System.out.println(Thread.currentThread().getName() + " must wait");
            try
            {
               condition.await();
            }
            catch(InterruptedException ex)
            {
               // ex.printStackTrace();
            }
         }
         System.out.println(Thread.currentThread().getName() + " ends waiting");

         // available = true
         content--;
         System.out.println(Thread.currentThread().getName() + " gets 1 content = " + content);
         available = false;
         condition.signalAll();
         return 1;
      }
      finally
      {
         lock.unlock();
      }
   }

   public int getZugriff()
   {
      return zugriff;
   }
}

Consumer, Producer sind wie bei wait() und notify(). Für die folgende Ausgabe wurden dem Consumer kürzere Pausen zugeteilt, sodaß er also mehr Zugriffe macht als der Producer. Trotzdem kommt es bei dieser Lösung nie zu einer vollständigen Leerung des Lagers.

producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
producer ends waiting
producer puts 1 content = 5
consumer ends waiting
consumer gets 1 content = 4
consumer must wait
37 Zugriffe
consumer must wait
java.lang.InterruptedException: sleep interrupted
producer end