Advanced   Java   Services Exchanger


Exchanger

Ein Exchanger ist eine Hilfsklasse zur Synchronisation von jeweils zwei Threads. Dazu stellt dies Klasse Exchanger zwei Methoden bereit.

ReturntypMethode
Vexchange(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.
Vexchange(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).


Beispiel

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