11.7.5 | Synchronisation auf das Ende von anderen Threads |
Zur Synchronisation auf das Ende eines anderen Threads definiert die Klasse Thread die Methode join(). Der Aufruft.join();blockiert den aufrufenden Thread so lange, bis der Thread t seine Ausführung beendet hat. Falls t zum Zeitpunkt des Aufrufs bereits beendet ist, bleibt die Methode ohne Auswirkungen, und der aufrufende Thread wird fortgesetzt.
Falls ein Thread auf das Ende mehrerer Threads warten muss, führt er eben mehrere join()-Aufrufe hintereinander aus. Die Reihenfolge der Aufrufe ist hierbei unwesentlich, weil die Gesamtwartezeit nur von dem Thread abhängt, der noch am längsten braucht.
Mit join() können auf einfache Weise Verarbeitungsketten implementiert werden, bei denen ein Thread auf die Fertigstellung eines Zwischenergebnisses warten muss, welches wiederum von anderen Threads berechnet wird. Solche Abhängigkeitsketten lassen sich mit Petri-Netzen modellieren.Ein solches Netz kann anhand dieser Regeln in Java-Threads umgesetzt werden:
- Die Verfügbarkeit einer Marke entspricht dem Ende eines Threads und dem Bereitstehen seines Ergebnisses.
- Die Stelle symbolisiert die Laufzeit des Threads. Die Marke steht erst nach Ablauf der Laufzeit zur Verfügung.
- Jeder eingehende Pfeil einer Transition steht für einen Thread, dessen Ergebnis von allen Threads hinter der Transition benötigt wird.
- Die von einer Transition abgehenden Pfeile stehen jeweils für einen Thread, der einen join() auf alle eingehenden Threads machen muss, bevor er seine Arbeit aufnimmt.
Bei der Implementierung ist zu beachten, dass der join()-Aufruf erst erfolgen darf, nachdem die Threads, auf die gewartet werden muss, gestartet sind. Abhängige Threads dürfen also erst nach den Threads gestartet werden, auf die sie warten müssen.
Das folgende Beispiel implementiert das Petri-Netz aus Abbildung 11.4. Als Basis dient wieder die Klasse TextThread vom Anfang des Kapitels. Ihr wird eine start()-Methode hinzugefügt, die zunächst auf das Ende der übergebenen Threads wartet und erst dann die Arbeit aufnimmt:public void start(JoinThread[] threads) { try { for(int i = 0; i < threads.length; i++) threads[i].join(); } catch(InterruptedException e) { } super.start(); }Im Hauptprogramm werden die einzelnen Threads erzeugt und gestartet. Bei den start()-Aufrufen werden die Threads übergeben, auf die gemäß dem Petri-Netz gewartet werden muss.t1 = new JoinThread("Thread 1"); t2 = new JoinThread("Thread 2"); t3 = new JoinThread("Thread 3"); t4 = new JoinThread("Thread 4"); t5 = new JoinThread("Thread 5"); t1.start(new JoinThread[]{}); t4.start(new JoinThread[]{}); t2.start(new JoinThread[]{t1}); t3.start(new JoinThread[]{t1}); t5.start(new JoinThread[]{t2, t3, t4});Material zum Beispiel
- Quelltexte: