Как дождаться завершения нескольких потоков?
каков способ просто дождаться завершения всего резьбового процесса? Например, допустим, у меня есть:
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
}
}
как я могу изменить это так main() метод делает паузу в комментарии, пока все потоки'run() методы выхода? Спасибо!
13 ответов:
вы помещаете все потоки в массив, запускаете их все, а затем имеете цикл
for(i = 0; i < threads.length; i++) threads[i].join();каждое соединение будет блокироваться до завершения соответствующего потока. Потоки могут завершаться в другом порядке, чем вы их соединяете, но это не проблема: когда цикл завершается, все потоки завершаются.
одним из способов было бы сделать
ListofThreads, создайте и запустите каждый поток, добавив его в список. Как только все будет запущено, вернитесь в список и вызовитеjoin()на каждого. Неважно, в каком порядке потоки завершают выполнение, все, что вам нужно знать, это то, что к моменту завершения второго цикла каждый поток будет завершен.лучший подход заключается в использовании ExecutorService и связанные с методы:
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С помощью ExecutorService (и все новые вещи из Java 5-х параллелизм коммунальные услуги) невероятно гибкий, и приведенный выше пример едва ли даже царапает поверхность.
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 } }
полностью избегайте класса Thread и вместо этого используйте более высокие абстракции, предоставляемые в java.утиль.одновременно
класс ExecutorService предоставляет метод invokeAll это, кажется, делать только то, что вы хотите.
вместо
join(), который является старым API, вы можете использовать CountDownLatch за. Я изменил ваш код, чтобы выполнить ваше требование.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(); } } }объяснение:
CountDownLatchбыло инициализировано с данным отсчетом 1000 согласно вашему требованию.каждый рабочий поток
DoSomethingInAThreadуменьшится наCountDownLatch, который был принят в конструктор.основной поток
CountDownLatchDemoawait()пока счет не станет нулевым. После того, как счет стал нулевым, вы получите ниже строки в выводе.In Main thread after completion of 1000 threadsдополнительная информация со страницы документации oracle
public void await() throws InterruptedExceptionзаставляет текущий поток ждать, пока защелка не будет отсчитана до нуля, если поток не будет прерван.
см. связанный вопрос SE для другого опции:
Как предложил Мартин к
java.util.concurrent.CountDownLatchКажется, быть лучшее решение для этого. Просто добавляя пример для того же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(); } } }
в зависимости от ваших потребностей, вы также можете проверить классы CountDownLatch и CyclicBarrier в java.утиль.concurrent пакет. Они могут быть полезны, если вы хотите, чтобы ваши потоки ждали друг друга, или если вы хотите более мелкозернистый контроль над тем, как ваши потоки выполняются (например, ожидание во внутреннем исполнении другого потока для установки некоторого состояния). Вы также можете использовать обратный отсчет, чтобы сигнализировать всем вашим потокам о запуске одновременно, вместо того, чтобы запускать их один по одному, когда вы повторяете свой цикл. В стандартных документах API есть пример этого, а также использование другого CountDownLatch для ожидания завершения выполнения всех потоков.
рассмотрите возможность использования
java.util.concurrent.CountDownLatch. Примеры в javadocs
Если вы делаете список потоков ,вы можете перебирать их и.соедините () против каждого, и ваш цикл закончится, когда все потоки будут иметь. Хотя я и не пробовал.
http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join()
Это был бы комментарий, но я пока не могу делать комментарии.
МартинK, мне любопытно, как вы будете использовать
ThreadGroup. Вы делали это раньше?Я вижу, что выше, вы предлагаете проверки
activeCount- отмена МартинvLöwisозабоченность по поводу опроса в сторону на данный момент, у меня есть еще одна проблема с .предостережение: я не пробовал использовать это, так что я нет эксперт по этому вопросу, но согласно javadocs, он возвращает оценка из числа активных потоков.
лично я бы не хотел пытаться построить систему на оценке. У вас есть еще одна мысль, как это сделать, или я ошибаюсь документации?
создайте объект потока внутри первого цикла for.
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(); }и то что все здесь говорят.
for(i = 0; i < threads.length; i++) threads[i].join();
вы можете сделать это с объектом "ThreadGroup" и его параметр activeCount:
в качестве альтернативы CountDownLatch за вы также можете использовать CyclicBarrier например
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(); } } }использовать CyclicBarrier в этом случае барьер.await () должен быть последним оператором, т. е. когда ваш поток завершает свою работу. CyclicBarrier можно использовать снова с его методом reset (). Цитировать документации:
CyclicBarrier поддерживает дополнительную выполняемую команду, которая выполняется один раз в точке барьера после последнего потока в партии прибывает, но до того, как какие-либо потоки будут освобождены. Это действие барьера полезно для обновления общего состояния, прежде чем любая из сторон продолжит.
Comments