2009-08-09 21 views
91

Was ist ein Weg, einfach zu warten, bis alle Gewindeprozesse beendet sind? Zum Beispiel, sagen wir mal, ich habe:Wie warten, bis mehrere Threads abgeschlossen sind?

public class DoSomethingInAThread implements Runnable{ 

    public static void main(String[] args) { 
     for (int n=0; n<1000; n++) { 
      Thread t = new Thread(new DoSomethingInAThread()); 
      t.start(); 
     } 
     // wait for all threads' run() methods to complete before continuing 
    } 

    public void run() { 
     // do something here 
    } 


} 

Wie verändern ich das so die main() Methode Pausen an der Kommentar, bis alle run() Methoden Ausfahrt Fäden? Vielen Dank!

Antwort

141

Sie stellen alle Themen in einem Array, beginnen sie alle, und haben dann eine Schleife

for(i = 0; i < threads.length; i++) 
    threads[i].join(); 

Jeder beitreten werden blockiert, bis der jeweilige Thread beendet hat. Threads können in einer anderen Reihenfolge abgeschlossen werden als Sie ihnen beitreten, aber das ist kein Problem: Wenn die Schleife beendet wird, sind alle Threads abgeschlossen.

+12

Thread-Gruppe ist natürlicher Weg, dies seit Java 5. –

+0

@Mykola: was * genau * ist der Vorteil der Verwendung einer Thread-Gruppe ? Nur weil die API da ist, heißt das nicht, dass du sie benutzen musst ... –

+0

Danke Leute. Ich werde in ThreadGroup schauen, aber mit Join() scheint so viel einfacher für die Arbeit mit dem vorhandenen Code! – DivideByHero

0

Sie können es mit dem Objekt "ThreadGroup" and its parameter activeCount:

+0

Nicht sicher, wie Sie genau vorgehen, es zu tun. Wenn Sie vorschlagen, activeCount in einer Schleife abzufragen: Das ist schlecht, da es beschäftigt ist - warten Sie (auch wenn Sie zwischen Umfragen schlafen - Sie erhalten dann einen Kompromiss zwischen Geschäft und Reaktionsfähigkeit). –

+0

@Martin v. Löwis: "Join wird nur auf einen einzigen Thread warten. Eine bessere Lösung könnte ein java.util.concurrent.CountDownLatch sein. Einfach den Latch mit die Anzahl auf die Anzahl der Arbeiter Threads initialisieren. Jeder Arbeiter Gewinde sollte rufen COUNTDOWN(), kurz bevor es beendet wird, und den Haupt-Thread einfach Anrufe warten(), die blockiert, bis der Zähler Null erreicht hat. das Problem mit join() ist auch, dass Sie nicht mehr Threads beginnen Hinzufügen dynamisch Die Liste wird mit einer gleichzeitigen Modifikation explodieren. " Ihre Lösung funktioniert gut für das Problem, aber nicht für allgemeine Zwecke. –

3

Wenn Sie eine Liste der Themen zu machen, können Sie eine Schleife durch sie und .join() gegen jeden, und der Schleife wird beendet, wenn alle Fäden haben. Ich habe es nicht versucht.

http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join()

+0

Hallo, es hat aus irgendeinem Grund nicht für mich funktioniert. Hier ist meine Frage: http://stackoverflow.com/users/5144855/ruchir-baronia –

31

Eine Möglichkeit wäre, einen List von Thread s zu erstellen, jeden Thread zu erstellen und zu starten und ihn der Liste hinzuzufügen. Sobald alles gestartet ist, durchlaufen Sie die Liste und rufen Sie join() auf jedem. Es spielt keine Rolle, in welcher Reihenfolge die Threads ausgeführt werden. Alles, was Sie wissen müssen, ist, dass zu dem Zeitpunkt, zu dem die zweite Schleife beendet ist, jeder Thread abgeschlossen sein wird.

Ein besserer Ansatz ist es, eine ExecutorService und die damit verbundene Methoden zu verwenden:

List<Callable> callables = ... // assemble list of Callables here 
           // Like Runnable but can return a value 
ExecutorService execSvc = Executors.newCachedThreadPool(); 
List<Future<?>> results = execSvc.invokeAll(callables); 
// Note: You may not care about the return values, in which case don't 
//  bother saving them 

eine ExecutorService Verwendung (und all die neuen Sachen von Java 5 ist concurrency utilities) unglaublich flexibel ist, und das obige Beispiel kaum noch Kratzer die Oberfläche.

+0

ThreadGroup ist der Weg zu gehen! Mit einer veränderbaren Liste werden Sie in Schwierigkeiten geraten (Synchronisation) –

+2

Was? Wie kommst du in Schwierigkeiten? Es ist nur änderbar (nur lesbar) durch den Thread, der den Start durchführt, so lange es die Liste nicht verändert * während es durchläuft, ist es in Ordnung. –

+0

Es hängt davon ab, wie Sie es verwenden. Wenn Sie die aufrufende Klasse in einem Thread verwenden, haben Sie Probleme. –

8

Vermeiden Sie die Thread-Klasse insgesamt und stattdessen die höheren Abstraktionen in java.util.concurrent vorgesehen verwenden

Die ExecutorService Klasse stellt die method invokeAll, die genau das zu tun scheint, was Sie wollen.

2

Dies wäre ein Kommentar, aber ich kann noch keine Kommentare machen.

MartinK, ich bin gespannt, wie Sie ThreadGroup verwenden würde. Hast du das schon mal gemacht?

Ich sehe, dass oben, Sie schlage vor, Check activeCount - abgesehen von MartinvLöwis ‚s Besorgnis über Polling für den Moment beiseite, ich habe ein anderes Anliegen mit activeCount selbst.

Caveat: Ich habe mit nicht versucht, so dass ich bin kein Experte in der Sache, sondern nach den javadocs, es gibt eine Schätzung der Anzahl der aktiven Threads.

Persönlich würde ich es verabscheuen zu versuchen, ein System auf einer Schätzung zu bauen. Hast du noch einen Gedanken darüber, wie es geht, oder missversteh ich das Javadoc?

+1

Der zurückgegebene Wert ist nur eine Schätzung, da sich die Anzahl der Threads dynamisch ändern kann, während diese Methode interne Datenstrukturen durchläuft und durch das Vorhandensein bestimmter Systemthreads beeinflusst werden kann. Es sollte für Überwachungszwecke in Ordnung sein. –

+0

Dies ist immer ein Problem in Threading-Umgebungen! Möchten Sie wirklich jeden Thread sperren, um einen "besseren" Wert zu erhalten? –

+0

Martin, ich stimme zu, dass eine Schätzung für die Anzahl der aktiven Threads für einen Überwachungsprozess in Ordnung ist. Aber wenn ich es nicht falsch interpretierte, wollte das OP warten, bis alle fertig waren - was nicht wie ein Monitor klingt. Für mich gibt es einen signifikanten Unterschied zwischen dem Wissen, dass sie alle vollständig sind, und dem Gedanken, dass sie alle vollständig sind (was das Beste ist, was Sie tun können, wenn Sie sich auf eine Schätzung verlassen). Ihre CountDownLatch-Idee ist jedoch hervorragend. – CPerkins

3

Je nach Ihren Bedürfnissen sollten Sie auch die Klassen CountDownLatch und CyclicBarrier im java.util.concurrent-Paket überprüfen. Sie können nützlich sein, wenn Sie möchten, dass Ihre Threads aufeinander warten, oder wenn Sie eine feinere Kontrolle darüber haben wollen, wie Ihre Threads ausgeführt werden (z. B. in ihrer internen Ausführung darauf warten, dass ein anderer Thread einen Status setzt). Sie können auch eine CountDownLatch-Funktion verwenden, um allen Threads zu signalisieren, dass sie gleichzeitig starten sollen, anstatt sie nacheinander zu starten, wenn Sie die Schleife durchlaufen. Die Standard-API-Dokumentation enthält ein Beispiel dafür sowie eine weitere CountDownLatch, um darauf zu warten, dass alle Threads ihre Ausführung abschließen.

22
import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 

public class DoSomethingInAThread implements Runnable 
{ 
    public static void main(String[] args) throws ExecutionException, InterruptedException 
    { 
     //limit the number of actual threads 
     int poolSize = 10; 
     ExecutorService service = Executors.newFixedThreadPool(poolSize); 
     List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>(); 

     for (int n = 0; n < 1000; n++) 
     { 
     Future f = service.submit(new DoSomethingInAThread()); 
     futures.add(f); 
     } 

     // wait for all tasks to complete before continuing 
     for (Future<Runnable> f : futures) 
     { 
     f.get(); 
     } 

     //shut down the executor service so that this thread can exit 
     service.shutdownNow(); 
    } 

    public void run() 
    { 
     // do something here 
    } 
} 
+0

arbeitete wie ein Charme ... Ich habe zwei Sätze von Threads, die nicht gleichzeitig wegen Problemen auf mehreren Cookies ausgeführt werden sollte. Ich habe dein Beispiel benutzt, um eine Reihe von Threads gleichzeitig auszuführen. Danke, dass du dein Wissen geteilt hast ... –

+0

Arbeite für mich, aber wie kann ich einen Fehler erfassen? – Dantalian

+0

@Dantalian - In Ihrer Runnable-Klasse (wahrscheinlich in der run-Methode) würden Sie alle aufgetretenen Ausnahmen erfassen und lokal speichern (oder eine Fehlermeldung/Bedingung speichern). Im Beispiel gibt f.get() Ihr Objekt zurück, das Sie an den ExecutorService gesendet haben. Ihr Objekt könnte eine Methode zum Abrufen von Ausnahmen/Fehlerbedingungen haben. Je nachdem, wie Sie das bereitgestellte Beispiel ändern, müssen Sie möglicherweise das mit f.get() gedrehte Objekt in den erwarteten Typ umwandeln. –

1

Erstellen Sie das Thread-Objekt innerhalb der ersten for-Schleife.

for (int i = 0; i < threads.length; i++) { 
    threads[i] = new Thread(new Runnable() { 
     public void run() { 
      // some code to run in parallel 
     } 
    }); 
    threads[i].start(); 
} 

Und dann so was jeder hier sagt.

for(i = 0; i < threads.length; i++) 
    threads[i].join(); 
3

Verwenden Sie java.util.concurrent.CountDownLatch. Beispiele in javadocs

+2

Können Sie mehr darüber herausfinden, wie das genau funktioniert? –

+0

Ist eine Verriegelung für Gewinde, funktioniert die Verriegelung mit einem Countdown. In der run() -Methode Ihres Threads deklarieren Sie explizit, dass auf CountDownLatch gewartet wird, um den Countdown auf 0 zu erreichen. Sie können denselben CountDownLatch in mehreren Threads verwenden, um sie gleichzeitig zu veröffentlichen. Ich weiß nicht, ob es das ist, was Sie brauchen, wollte es nur erwähnen, weil es nützlich ist, wenn Sie in einer Multithread-Umgebung arbeiten. –

+0

Vielleicht sollten Sie diese Erklärung in den Text Ihrer Antwort einfügen? –

5

Wie Martin K vorgeschlagen java.util.concurrent.CountDownLatch scheint eine bessere Lösung dafür zu sein. Nur das Hinzufügen ein Beispiel für die gleiche

 public class CountDownLatchDemo 
{ 

    public static void main (String[] args) 
    { 
     int noOfThreads = 5; 
     // Declare the count down latch based on the number of threads you need 
     // to wait on 
     final CountDownLatch executionCompleted = new CountDownLatch(noOfThreads); 
     for (int i = 0; i < noOfThreads; i++) 
     { 
      new Thread() 
      { 

       @Override 
       public void run() 
       { 

        System.out.println("I am executed by :" + Thread.currentThread().getName()); 
        try 
        { 
         // Dummy sleep 
         Thread.sleep(3000); 
         // One thread has completed its job 
         executionCompleted.countDown(); 
        } 
        catch (InterruptedException e) 
        { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 

      }.start(); 
     } 

     try 
     { 
      // Wait till the count down latch opens.In the given case till five 
      // times countDown method is invoked 
      executionCompleted.await(); 
      System.out.println("All over"); 
     } 
     catch (InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 
    } 

} 
6

statt join(), die eine alte API ist, können Sie CountDownLatch verwenden. Ich habe Ihren Code wie folgt geändert, um Ihre Anforderung zu erfüllen.

import java.util.concurrent.*; 
class DoSomethingInAThread implements Runnable{ 
    CountDownLatch latch; 
    public DoSomethingInAThread(CountDownLatch latch){ 
     this.latch = latch; 
    } 
    public void run() { 
     try{ 
      System.out.println("Do some thing"); 
      latch.countDown(); 
     }catch(Exception err){ 
      err.printStackTrace(); 
     } 
    } 
} 

public class CountDownLatchDemo { 
    public static void main(String[] args) { 
     try{ 
      CountDownLatch latch = new CountDownLatch(1000); 
      for (int n=0; n<1000; n++) { 
       Thread t = new Thread(new DoSomethingInAThread(latch)); 
       t.start(); 
      } 
      latch.await(); 
      System.out.println("In Main thread after completion of 1000 threads"); 
     }catch(Exception err){ 
      err.printStackTrace(); 
     } 
    } 
} 

Erklärung:

  1. CountDownLatch wurde mit gegebenen Zählung 1000 als pro Ihre Anforderung initialisiert.

  2. Jeder Worker-Thread DoSomethingInAThread dekrementiert den CountDownLatch, der im Konstruktor übergeben wurde.

  3. Hauptthread CountDownLatchDemoawait() bis die Zählung Null geworden ist. Sobald die Zählung zu Null geworden ist, werden Sie in der Ausgabe unterhalb der Linie ankommen.

    In Main thread after completion of 1000 threads 
    

Weitere Daten aus der Oracle-Dokumentation Seite

public void await() 
      throws InterruptedException 

Ursachen der aktuelle Thread warten, bis die Verriegelung bis auf Null gezählt hat, es sei denn, der Faden unterbrochen wird.

Siehe verwandte SE Frage für weitere Optionen:

wait until all threads finish their work in java

0

Als Alternative zu CountDownLatch Sie auch CyclicBarrier zum Beispiel verwenden können

public class ThreadWaitEx { 
    static CyclicBarrier barrier = new CyclicBarrier(100, new Runnable(){ 
     public void run(){ 
      System.out.println("clean up job after all tasks are done."); 
     } 
    }); 
    public static void main(String[] args) { 
     for (int i = 0; i < 100; i++) { 
      Thread t = new Thread(new MyCallable(barrier)); 
      t.start(); 
     }  
    } 

}  

class MyCallable implements Runnable{ 
    private CyclicBarrier b = null; 
    public MyCallable(CyclicBarrier b){ 
     this.b = b; 
    } 
    @Override 
    public void run(){ 
     try { 
      //do something 
      System.out.println(Thread.currentThread().getName()+" is waiting for barrier after completing his job."); 
      b.await(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } catch (BrokenBarrierException e) { 
      e.printStackTrace(); 
     } 
    }  
} 

Um CyclicBarrier in diesem Fall barrier.await() zu verwenden, sollte die letzte Anweisung das heißt, wenn der Thread mit seiner Arbeit erledigt ist. CyclicBarrier kann mit seiner reset() - Methode erneut verwendet werden. Um javadocs zu zitieren:

Eine CyclicBarrier unterstützt einen optionalen Runnable-Befehl, der einmal pro Sperrpunkt ausgeführt wird, nachdem der letzte Thread in der Gruppe eingetroffen ist, aber bevor Threads freigegeben werden. Diese Barrier-Aktion ist nützlich, um den Shared-State zu aktualisieren, bevor eine der Parteien fortfährt.

+0

Ich denke nicht, dass das ein gutes Beispiel für CyclicBarrier ist. Warum verwenden Sie einen Thread.sleep() -Aufruf? – Guenther

+0

@Guenther - Ja, ich habe den Code geändert, um die Anforderung zu erfüllen. – shailendra1118

+0

CyclicBarrier ist keine Alternative zu CountDownLatch. Wenn Threads wiederholt heruntergezählt werden müssen, sollten Sie einen CyclicBarrier erstellen, andernfalls CountDownLatch (sofern nicht eine zusätzliche Abstraktion der Ausführung erforderlich ist, und an dieser Stelle sollten Sie sich die Dienste auf höherer Ebene ansehen). – Elysiumplain

Verwandte Themen