Was bedeutet dieser Thread-Join-Code?

156

Was bedeuten in diesem Code die beiden Verknüpfungen und Unterbrechungen? t1.join()Ursachen t2zu stoppen, bis t1beendet?

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread t2 = new Thread(new EventThread("e2"));
t2.start();
while (true) {
   try {
      t1.join();
      t2.join();
      break;
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}
user697911
quelle
3
Warum haben Sie die while-Schleife verwendet, da sie blockiert und auf die Beendigung des Threads wartet?
Mehdi
@ MahdiElMasaoudi Ich nehme an, weiter zu warten, auch wenn der Thread unterbrochen ist? Wahrscheinlich keine gute Möglichkeit, das zu tun
forresthopkinsa
Denken Sie daran, hier eine Antwort zu akzeptieren, wenn dies hilfreich war.
Grau
Wie bereits erwähnt, müssen Sie while(true)die joinMethode nicht aufrufen .
Chaklader Asfak Arefe

Antworten:

311

Was bedeutet dieser Thread-Join-Code?

Um aus der Thread.join()Methode javadocs zu zitieren :

join() Wartet darauf, dass dieser Thread stirbt.

Es gibt einen Thread, in dem Ihr Beispielcode ausgeführt wird, der wahrscheinlich der Hauptthread ist .

  1. Der Haupt-Thread erstellt und startet die t1und t2-Threads. Die beiden Threads laufen parallel.
  2. Der Haupt-Thread ruft t1.join()auf, um zu warten, bis der t1Thread beendet ist.
  3. Der t1Thread wird abgeschlossen und die t1.join()Methode wird im Hauptthread zurückgegeben. Beachten Sie, dass t1dies möglicherweise bereits vor dem join()Tätigen des Anrufs beendet wurde. In diesem Fall wird der join()Anruf sofort zurückgegeben.
  4. Der Haupt-Thread ruft t2.join()auf, um zu warten, bis der t2Thread beendet ist.
  5. Der t2Thread wird abgeschlossen (oder möglicherweise vor dem t1Thread abgeschlossen) und die t2.join()Methode wird im Hauptthread zurückgegeben.

Es ist wichtig zu verstehen, dass die t1und t2-Threads parallel ausgeführt wurden, aber der Hauptthread, der sie gestartet hat, muss warten, bis sie beendet sind, bevor sie fortgesetzt werden können. Das ist ein gängiges Muster. Auch t1und / oder t2hätte beendet werden können, bevor der Haupt-Thread sie aufruft join(). Wenn ja, dann join()wird nicht warten, sondern sofort zurückkehren.

t1.join() bedeutet, dass t2 stoppt, bis t1 endet?

Nein. Der aufrufende Haupt- Thread t1.join()wird nicht mehr ausgeführt und wartet, bis der t1Thread beendet ist. Der t2Thread läuft parallel und ist von t1oder dem t1.join()Aufruf überhaupt nicht betroffen .

Im Hinblick auf die try / catch, das join()wirft InterruptedExceptionwas bedeutet , dass der Haupt - Thread, der ruft join()kann selbst von einem anderen Thread unterbrochen werden.

while (true) {

Die Verknüpfungen in einer whileSchleife zu haben, ist ein seltsames Muster. Normalerweise führen Sie den ersten Join und dann den zweiten Join durch, um die jeweils InterruptedExceptionentsprechenden Verknüpfungen durchzuführen . Sie müssen sie nicht in eine Schleife setzen.

Grau
quelle
24
+1 Das ist ein sehr seltsames Muster und kann wahrscheinlich entfernt werden.
m0skit0
3
Wenn t1 zuerst fertig ist, dann t2 fertig. Das scheint ein sequentieller Prozess zu sein. Ein Thread endet zuerst, dann der andere. Was bringt Multiple Threading?
user697911
9
Weil das t1und t2parallel laufen kann. Es ist nur so, dass mainbeide fertig sein müssen, bevor es weitergehen kann. Das ist ein typisches Muster @ user697911.
Grau
3
Die whileSchleife ist da, weil sie (glaube ich) die join()Anrufe wiederholen möchte, wenn einer unterbrochen wird? Ich würde es auf keinen Fall so schreiben @ user697911.
Grau
5
Die Schleife ist da, um sicherzustellen, dass beide t1und t2beenden. Dh. Wenn das t1geworfen wird InterruptedException, wird es zurückgeschleift und wartet auf t2. Eine Alternative besteht darin, in ihrem Try-Catch auf beide Threads zu warten, damit die Schleife vermieden werden kann. Abhängig EventThreaddavon kann es auch sinnvoll sein, dies auf diese Weise zu tun, da wir zwei Threads ausführen, nicht einen.
Michael Bisbjerg
68

Dies ist eine beliebte Java-Interviewfrage .

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread e2 = new Thread(new EventThread("e2"));
t2.start();

while (true) {
    try {
        t1.join(); // 1
        t2.join(); // 2  These lines (1,2) are in in public static void main
        break;
    }
}

t1.join()bedeutet, t1 sagt so etwas wie " Ich möchte zuerst fertig werden ". Gleiches gilt für t2. Unabhängig davon, wer gestartet t1oder t2Thread (in diesem Fall die mainMethode), wartet main bis t1und t2beendet seine Aufgabe.

Ein wichtiger Punkt jedoch zu notieren, t1und t2selbst kann parallel unabhängig von der Join - Aufrufsequenz läuft auf t1und t2. Es ist der main/daemonThread, der warten muss .

AmitG
quelle
3
Gutes Beispiel. Über "kann parallel laufen": Na und? Es ist wichtig, dass der Haupt-Thread ZUERST auf t1 und DANN auf t2 wartet. Es ist wirklich egal, was t1 oder t2 tun (aus der Perspektive des Hauptthreads)
Alex
1
Sie erwähnen den Thread "main / daemon". Das Wichtigste verstehe ich, aber der Daemon hat nichts damit zu tun. Der Hauptthread ist kein Daemon.
Grau
1
t1.join(); t2.join();Der Thread, der die Joins ausführt, kann erst fortgesetzt werden, wenn beide Threads beendet wurden. Da an anderer Stelle kein sehr ungewöhnlicher Code vorhanden ist, spielt die Reihenfolge der Verknüpfungen keine Rolle.
David Schwartz
Wird t2.join () also erst aufgerufen, wenn t1 fertig ist?
Leo Droidcoder
Mit anderen Worten, wenn wir die Ausführung von t1- und t2-Threads "serialisieren" wollen, müssen wir t1.join () direkt nach t1.start () platzieren, da der Hauptthread t1 (und danach t2) gestartet hat, und es natürlich aus entfernen versuchen / fangen. Dies führt natürlich zu einem Verlust der Parallelität.
Dobrivoje
47

join()bedeutet, auf den Abschluss eines Threads zu warten. Dies ist eine Blockermethode. Ihr Haupt-Thread (derjenige, der das macht join()) wartet in der t1.join()Leitung, bis t1seine Arbeit beendet ist, und macht dann dasselbe für t2.join().

Avi
quelle
29

Ein Bild sagt mehr als tausend Worte.

    Main thread-->----->--->-->--block##########continue--->---->
                 \                 |               |
sub thread start()\                | join()        |
                   \               |               |
                    ---sub thread----->--->--->--finish    

Ich hoffe, nützlich, für weitere Details klicken Sie hier

xxy
quelle
3
Klar und präzise.
Dobrivoje
10

Wenn der Thread tA tB.join () aufruft, wartet er nicht nur darauf, dass tB stirbt oder tA selbst unterbrochen wird, sondern erstellt eine Beziehung zwischen der letzten Anweisung in tB und der nächsten Anweisung nach tB.join () im tA-Thread.

Alle Aktionen in einem Thread werden ausgeführt, bevor ein anderer Thread erfolgreich von einem join () in diesem Thread zurückgegeben wird.

Es bedeutet Programm

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        threadB.join();

        while (true) 
            System.out.print(sharedVar);
    }
}

Immer drucken

>> 1111111111111111111111111 ...

Aber programmieren

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        // threadB.join();  COMMENT JOIN

        while (true)
            System.out.print(sharedVar);
    }
}

Kann nicht nur drucken

>> 0000000000 ... 000000111111111111111111111111 ...

Aber

>> 00000000000000000000000000000000000000000000 ... 

Immer nur '0'.

Da für das Java-Speichermodell kein "Übertragen" des neuen Werts von "sharedVar" von threadB zum Hauptthread ohne Heppens-before-Beziehung erforderlich ist (Thread-Start, Thread-Verknüpfung, Verwendung des Schlüsselworts "synchronisiert", Verwendung von AtomicXXX-Variablen usw.).

Ivan Golovach
quelle
5

Einfach ausgedrückt:
t1.join()kehrt nach t1Abschluss zurück.
Es macht nichts zum Threading t1, außer warten, bis es fertig ist.
Natürlich wird der folgende Code t1.join()erst nach der t1.join()Rückgabe ausgeführt.

c0der
quelle
1
wird erst ausgeführt, nachdem t1.join () +1
mohsen.nour
3

Von der Oracle-Dokumentationsseite auf Joins

Mit dieser joinMethode kann ein Thread auf den Abschluss eines anderen warten.

Wenn t1 ein ThreadObjekt ist, dessen Thread gerade ausgeführt wird,

t1.join() : causes the current thread to pause execution until t1's thread terminates.

Wenn t2 ein ThreadObjekt ist, dessen Thread gerade ausgeführt wird,

t2.join(); causes the current thread to pause execution until t2's thread terminates.

joinAPI ist eine Low-Level-API, die in früheren Versionen von Java eingeführt wurde. Viele Dinge wurden im Laufe der Zeit (insbesondere mit jdk 1.5 Release) in Bezug auf die Parallelität geändert.

Sie können dasselbe mit der API java.util.concurrent erreichen. Einige Beispiele sind

  1. Verwenden von invokeAll onExecutorService
  2. Mit CountDownLatch
  3. Verwenden von ForkJoinPool oder newWorkStealingPool von Executors(seit Java 8)

Siehe verwandte SE-Fragen:

Warten Sie, bis alle Threads ihre Arbeit in Java beendet haben

Ravindra Babu
quelle
1

Für mich war das Verhalten von Join () immer verwirrend, weil ich mich daran erinnern wollte, wer auf wen warten wird. Versuchen Sie nicht, sich so daran zu erinnern.

Darunter befindet sich der reine Mechanismus wait () und notify ().

Wir alle wissen, dass, wenn wir wait () für ein Objekt (t1) aufrufen, das aufrufende Objekt (main) an den Warteraum gesendet wird (blockierter Zustand).

Hier ruft der Hauptthread join () auf, das unter dem Deckmantel wait () ist. Der Haupt-Thread wartet also, bis er benachrichtigt wird. Die Benachrichtigung erfolgt durch t1, wenn der Lauf beendet ist (Thread-Abschluss).

Nach Erhalt der Benachrichtigung verlässt main den Warteraum und setzt die Ausführung fort.

Arjun Patil
quelle
0

Ich hoffe es hilft!

package join;

public class ThreadJoinApp {

    Thread th = new Thread("Thread 1") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());
            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    Thread th2 = new Thread("Thread 2") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());

            //Thread 2 waits until the thread 1 successfully completes.
            try {
            th.join();
            } catch( InterruptedException ex) {
                System.out.println("Exception has been caught");
            }

            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    public static void main(String[] args) {
        ThreadJoinApp threadJoinApp = new ThreadJoinApp();
        threadJoinApp.th.start();
        threadJoinApp.th2.start();
    }

    //Happy coding -- Parthasarathy S
}
Parthasarathy S.
quelle
-3

Nehmen wir an, unser Haupt-Thread startet die Threads t1 und t2. Wenn nun t1.join () aufgerufen wird, hält sich der Haupt-Thread an, bis der Thread t1 stirbt, und setzt sich dann fort. In ähnlicher Weise wird der Hauptthread beim Ausführen von t2.join () erneut angehalten, bis der Thread t2 stirbt und dann fortgesetzt wird.

So funktioniert es also.

Auch die while-Schleife wurde hier nicht wirklich benötigt.

Skmangalam
quelle