Advanced Java Services | Exchanger |
Ein Exchanger ist eine Hilfsklasse zur Synchronisation von jeweils zwei Threads. Dazu stellt dies Klasse Exchanger zwei Methoden bereit.
Returntyp | Methode |
---|---|
V | exchange(V x)
Waits for another thread to arrive at this exchange point (unless the current thread is interrupted), and then transfers the given object to it, receiving its object in return. |
V | exchange(V x, long timeout, TimeUnit unit)
Waits for another thread to arrive at this exchange point (unless the current thread is interrupted or the specified waiting time elapses), and then transfers the given object to it, receiving its object in return. |
Die Methode blockiert (im zweiten Fall nur eine gewisse Zeit) und wartet auf einen Partnerthread, der ebenfalls über die Methode exchange() verfügt und ein Objekt zum Tausch anbietet. Wird der Synchronisationspunkt erreicht werden die beiden Objekte getauscht. Die Threads wissen nicht, mit welchem Thread sie ein Objekt tauschen (es sei denn, diese Information steht im getauschten Objekt selbst).
Das folgende Runnable erhält einen Exchanger über den Konstruktor und wartet mit der Methode exchange() auf einen Datenaustausch.
class Task implements Runnable { Exchanger<String> exchanger; String name; String toExchange; public Task(Exchanger<String> exchanger, String name, String toExchange) { this.name = name; this.toExchange = toExchange; this.exchanger = exchanger; } @Override public void run() { try { TimeUnit.SECONDS.sleep(1); System.out.println(name + " waits for exchange"); toExchange = exchanger.exchange(toExchange); System.out.println(name + " received " + toExchange); TimeUnit.SECONDS.sleep(1); } catch(InterruptedException ex) { ex.printStackTrace(); } } }
In main legen wir einen Exchanger und einen ExecutorService an. Es werden Strings ausgetauscht, die eine Meldung enthalten. In den Strings steht der Absender sodaß die Konsolausgaben erkennen lassen, wie der Austausch stattfindet.
public static void main(String[] args) { Exchanger<String> exchanger = new Exchanger<String>(); ExecutorService executorService = Executors.newFixedThreadPool(4); executorService.execute(new Task(exchanger, "Task1", "from Task1")); executorService.execute(new Task(exchanger, "Task2", "from Task2")); executorService.execute(new Task(exchanger, "Task3", "from Task3")); executorService.execute(new Task(exchanger, "Task4", "from Task4")); executorService.shutdown(); // blockiert nicht, beendet den Executorservice. // aber nur, wenn kein Thread blockiert System.out.println("fin"); }
Es folgen zwei Ablaufbeispiele mit vier Tasks.
Ablaufbeispiele mit vier Tasks fin Task2 waits for exchange Task1 waits for exchange Task3 waits for exchange Task2 received from Task1 Task1 received from Task2 Task4 waits for exchange Task4 received from Task3 Task3 received from Task4 oder fin Task1 waits for exchange Task3 waits for exchange Task2 waits for exchange Task3 received from Task1 Task1 received from Task3 Task4 waits for exchange Task4 received from Task2 Task2 received from Task4
Läßt man das Beispiel mit drei Tasks laufen, so bleibt ein Thread übrig, der nicht tauschen kann:
blaufbeispiele mit drei Tasks fin Task3 waits for exchange Task1 waits for exchange Task2 waits for exchange Task3 received from Task1 Task1 received from Task3 oder fin Task1 waits for exchange Task2 waits for exchange Task3 waits for exchange Task1 received from Task2 Task2 received from Task1