it-swarm.com.de

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

Ich schreibe eine Anwendung, die 5 Threads enthält, die gleichzeitig einige Informationen aus dem Web abrufen und 5 verschiedene Felder in einer Pufferklasse füllen.
Ich muss die Pufferdaten überprüfen und in einer Datenbank speichern, wenn alle Threads ihre Arbeit beendet haben.
Wie kann ich das tun (wird benachrichtigt, wenn alle Threads ihre Arbeit beendet haben)? 

74
RYN

Ich gehe davon aus, einen ExecutorService zu verwenden, um Pools von Threads zu verwalten.

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.
93
Peter Lawrey

Sie können join zu den Threads. Der Join blockiert, bis der Thread abgeschlossen ist.

for (Thread thread : threads) {
    thread.join();
}

Beachten Sie, dass join eine InterruptedException auslöst. Sie müssen entscheiden, was in diesem Fall geschehen soll (versuchen Sie beispielsweise, die anderen Threads abzubrechen, um unnötige Arbeit zu vermeiden).

42
Mark Byers

Schauen Sie sich verschiedene Lösungen an. 

  1. join() API wurde in früheren Java-Versionen eingeführt. Mit diesem concurrent -Paket stehen seit dem Release JDK 1.5 einige gute Alternativen zur Verfügung.

  2. ExecutorService # invokeAll ()

    Führt die angegebenen Aufgaben aus und gibt eine Liste der Futures zurück, die ihren Status und die Ergebnisse enthalten, wenn alles abgeschlossen ist.

    Codebeispiele finden Sie in dieser verwandten SE-Frage:

    Wie verwende ich invokeAll (), damit alle Thread-Pools ihre Aufgabe erledigen können?

  3. CountDownLatch

    Eine Synchronisationshilfe, mit der ein oder mehrere Threads warten können, bis ein Satz von Vorgängen in anderen Threads abgeschlossen ist.

    Ein CountDownLatch wird mit einer bestimmten Anzahl initialisiert. Die wait-Methoden blockieren, bis der aktuelle Zählerstand aufgrund von Aufrufen der countDown()-Methode Null erreicht, wonach alle wartenden Threads freigegeben werden und alle darauffolgenden Aufrufe von await sofort zurückkehren. Dies ist ein One-Shot-Phänomen - die Zählung kann nicht zurückgesetzt werden. Wenn Sie eine Version benötigen, die den Zähler zurücksetzt, sollten Sie einen CyclicBarrier verwenden.

    Beziehen Sie sich auf diese Frage für die Verwendung von CountDownLatch

    Wie kann man auf einen Thread warten, der seinen eigenen Thread hervorbringt?

  4. ForkJoinPool oder newWorkStealingPool () in Executors

  5. Durchlaufen alle Future Objekte, die nach dem Senden an ExecutorService erstellt wurden. 

19
Ravindra babu

Abgesehen von Thread.join(), das von anderen vorgeschlagen wurde, hat Java 5 das Executor-Framework eingeführt. Dort arbeiten Sie nicht mit Thread-Objekten. Stattdessen übergeben Sie Ihre Callable- oder Runnable-Objekte an einen Executor. Es gibt einen speziellen Executor, der mehrere Aufgaben ausführen und deren Ergebnisse in eine falsche Reihenfolge bringen soll. Das ist die ExecutorCompletionService :

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

Dann können Sie wiederholt take() aufrufen, bis keine Future<?>-Objekte mehr zurückgegeben werden müssen, was bedeutet, dass alle Objekte abgeschlossen sind.


Eine andere Sache, die je nach Szenario relevant sein kann, ist CyclicBarrier .

Eine Synchronisationshilfe, mit der ein Satz Threads warten kann, bis ein gemeinsamer Barrierepunkt erreicht ist. CyclicBarriers sind in Programmen nützlich, an denen eine Gruppe von Threads fester Größe beteiligt ist, die gelegentlich aufeinander warten müssen. Die Barriere wird als zyklisch bezeichnet, da sie nach dem Freigeben der wartenden Threads erneut verwendet werden kann. 

10
Bozho

Eine andere Möglichkeit ist das Objekt CountDownLatch , das für einfache Situationen nützlich ist: Da Sie die Anzahl der Threads im Voraus kennen, initialisieren Sie sie mit der entsprechenden Anzahl und übergeben die Referenz des Objekts an jeden Thread.
Bei Beendigung seiner Aufgabe ruft jeder Thread CountDownLatch.countDown() auf, wodurch der interne Zähler dekrementiert wird. Nachdem der Haupt-Thread alle anderen gestartet hat, sollte er den Aufruf von CountDownLatch.await() blockieren. Sie wird freigegeben, sobald der interne Zähler 0 erreicht hat.

Beachten Sie, dass mit diesem Objekt auch eine InterruptedException geworfen werden kann.

9
interDist

Sie machen

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

Nach dieser for-Schleife können Sie sicher sein, dass alle Threads ihre Jobs abgeschlossen haben.

8
aioobe

Speichern Sie die Thread-Objekte in einer Sammlung (z. B. einer Liste oder einem Set). Führen Sie dann eine Schleife durch die Sammlung, sobald die Threads gestartet sind, und rufen Sie join () in den Threads auf.

4
esaj

Obwohl dies für das Problem von OP nicht relevant ist, können Sie einen Exchanger

In meinem Fall musste ich den übergeordneten Thread anhalten, bis der untergeordnete Thread etwas tat, z. seine Initialisierung abgeschlossen. A CountDownLatch funktioniert ebenfalls gut.

Zu diesem Zweck können Sie die Methode Thread # join verwenden.

2
Narendra Yadala

Ich hatte ein ähnliches Problem und habe Java 8 parallelStream verwendet.

requestList.parallelStream().forEach(req -> makeRequest(req));

Es ist sehr einfach und lesbar ..__ Hinter den Kulissen verwendet es den Fork-Join-Pool der Standard-JVM, was bedeutet, dass alle Threads warten, bevor sie fortfahren. Für meinen Fall war es eine ordentliche Lösung, weil es der einzige parallelStream in meiner Anwendung war. Wenn Sie mehrere parallelStream gleichzeitig ausführen, lesen Sie bitte den folgenden Link.

Weitere Informationen zu parallelen Streams hier .

1
Madis Pukkonen

versuchen Sie das, wird funktionieren.

  Thread[] threads = new Thread[10];

  List<Thread> allThreads = new ArrayList<Thread>();

  for(Thread thread : threads){

        if(null != thread){

              if(thread.isAlive()){

                    allThreads.add(thread);

              }

        }

  }

  while(!allThreads.isEmpty()){

        Iterator<Thread> ite = allThreads.iterator();

        while(ite.hasNext()){

              Thread thread = ite.next();

              if(!thread.isAlive()){

                   ite.remove();
              }

        }

   }
1
Jeyaraj.J

Warten/blockieren Sie den Thread Main, bis einige andere Threads ihre Arbeit beendet haben.

Wie @Ravindra babu sagte, kann dies auf verschiedene Arten erreicht werden, jedoch anhand von Beispielen.

  • Java.lang.Thread . join () Seit: 1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
    
  • Java.util.concurrent.CountDownLatch Seit: 1.5

    • .countDown() "Verringert die Anzahl der Latch-Gruppen.
    • .await() "Die Warte-Methoden werden blockiert, bis der aktuelle Zählerstand Null erreicht.

    Wenn Sie latchGroupCount = 4 erstellt haben, sollte countDown() viermal aufgerufen werden, um den Wert 0 zu ermitteln. Damit gibt await() die blockierenden Threads frei.

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }
    

Beispielcode der Threaded-Klasse LatchTask. Um den Ansatz zu testen, verwenden Sie joiningThreads(); und latchThreads(); von der Hauptmethode.

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • CyclicBarriers Eine Synchronisationshilfe, mit der eine Gruppe von Threads auf einander warten kann, um einen gemeinsamen Barrierepunkt zu erreichen. CyclicBarriers sind nützlich in Programmen, in denen eine Gruppe von Threads fester Größe gelegentlich aufeinander warten muss. Die Barriere wird als zyklisch bezeichnet, da sie wiederverwendet werden kann, nachdem die wartenden Threads freigegeben wurden.
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    
    Verweisen Sie beispielsweise auf diese Concurrent_ParallelNotifyies -Klasse.

  • Executer-Framework: Mit ExecutorService können wir einen Thread-Pool erstellen und den Fortschritt der asynchronen Tasks mit Future verfolgen.

    • submit(Runnable), submit(Callable), die Future Object zurückgeben. Mit der Funktion future.get() können wir den Hauptthread blockieren, bis der Arbeitsthread seine Arbeit beendet hat.

    • invokeAll(...) - gibt eine Liste der zukünftigen Objekte zurück, über die Sie die Ergebnisse der Ausführungen der einzelnen Callable erhalten können.

Beispiel finden der Verwendung von Interfaces Runnable, Callable with Executor Framework.


@Siehe auch

1
Yash

Ein Executor-Service kann zur Verwaltung mehrerer Threads verwendet werden, einschließlich Status und Abschluss. Siehe http://programmingexamples.wikidot.com/executorservice

1
user974465

Ich habe eine kleine Hilfsmethode erstellt, um darauf zu warten, dass ein paar Threads fertig sind:

public static void waitForThreadsToFinish(Thread... threads) {
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
0
Alon Gouldman

Die vorhandenen Antworten könnten join() für jeden Thread sein.

Es gibt jedoch mehrere Möglichkeiten, das Thread-Array/die Thread-Liste abzurufen:

  • Fügen Sie den Thread bei der Erstellung einer Liste hinzu.
  • Verwenden Sie ThreadGroup, um die Threads zu verwalten.

Der folgende Code verwendet den Ansatz ThreadGruop. Zuerst wird eine Gruppe erstellt. Wenn Sie dann einen Thread erstellen, geben Sie die Gruppe im Konstruktor an. Später kann das Thread-Array über ThreadGroup.enumerate() abgerufen werden.


Code

SyncBlockLearn.Java

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * synchronized block - learn,
 *
 * @author eric
 * @date Apr 20, 2015 1:37:11 PM
 */
public class SyncBlockLearn {
    private static final int TD_COUNT = 5; // thread count
    private static final int ROUND_PER_THREAD = 100; // round for each thread,
    private static final long INC_DELAY = 10; // delay of each increase,

    // sync block test,
    @Test
    public void syncBlockTest() throws InterruptedException {
        Counter ct = new Counter();
        ThreadGroup tg = new ThreadGroup("runner");

        for (int i = 0; i < TD_COUNT; i++) {
            new Thread(tg, ct, "t-" + i).start();
        }

        Thread[] tArr = new Thread[TD_COUNT];
        tg.enumerate(tArr); // get threads,

        // wait all runner to finish,
        for (Thread t : tArr) {
            t.join();
        }

        System.out.printf("\nfinal count: %d\n", ct.getCount());
        Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
    }

    static class Counter implements Runnable {
        private final Object lkOn = new Object(); // the object to lock on,
        private int count = 0;

        @Override
        public void run() {
            System.out.printf("[%s] begin\n", Thread.currentThread().getName());

            for (int i = 0; i < ROUND_PER_THREAD; i++) {
                synchronized (lkOn) {
                    System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
                }
                try {
                    Thread.sleep(INC_DELAY); // wait a while,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.printf("[%s] end\n", Thread.currentThread().getName());
        }

        public int getCount() {
            return count;
        }
    }
}

Der Haupt-Thread wartet, bis alle Threads in der Gruppe beendet sind.

0
Eric Wang