Advanced Java Services | await() und signal() |
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 enthält await() und signal()-Methoden, die das Pendant zu wait() und notify() bilden.
java.util.concurrent.locks.Condition | |
---|---|
Returntyp | Name der Methode |
void | await() Causes the current thread to wait until it is signalled or interrupted. |
boolean | await(long time, TimeUnit unit) Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses. |
long | awaitNanos(long nanosTimeout) Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses. |
void | awaitUninterruptibly() Causes the current thread to wait until it is signalled. |
boolean | awaitUntil(Date deadline) Causes the current thread to wait until it is signalled or interrupted, or the specified deadline elapses. |
void | signal() Wakes up one waiting thread. |
void | signalAll() Wakes up all waiting threads. |
Die await() Methode bietet mehr Konfigurationsmöglichkeiten als die ältere wait()-Methode.
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.
await() und signal() entsprechen zwar dem Paar wait() und notify(). Es muß jedoch etwas mehr beachtet werden.
Die folgenden Beispiele sind die Übertragung der Beispiele aus dem Kapitel wait() und notify():
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
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
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.
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