Как узнать точное время, необходимое для завершения потока
У меня есть две темы T1 и T2. Оба они делают некоторые вычисления, и я пытаюсь заблокировать основной поток до конца t1 и t2. Я использовал.awaitTermination(), как показано ниже, но проблема в том, что, несмотря на то, что это оператор if,.awaitTermination() идет в бесконечном цикле.
пожалуйста, помогите мне найти, что происходит. и я должен указать количество времени, не зная точного времени, которое требуется t1 и t2, чтобы закончить?
executor.execute(new RunnableClass(bgr,3))
executor.execute(new RunnableClass(bgr,7))
executor.shutdown();
if (executor.awaitTermination(3, TimeUnit.SECONDS)) {
print("terminated")
}
3 ответа
Если вы используете Java 8, вы можете использовать CompletableFuture<T>
вместо. Они определяют некоторые полезные методы, такие как объединение, чтобы дождаться их выполнения и связать их вместе. Ваш пример будет выглядеть так:
final CompletableFuture<Void> future1 = CompletableFuture.runAsync(new RunnableClass(bgr, 3), executor);
final CompletableFuture<Void> future2 = CompletableFuture.runAsync(new RunnableClass(bgr, 7), executor);
CompletableFuture.allOf(future1, future2).join(); // blocks until both finished
executor.shutdown();
Отличное введение можно найти здесь.
Прежде всего, время, которое вы проходите awaitTermination
является приблизительным значением, и оно всегда должно быть БОЛЬШЕ, чем ваше наихудшее совокупное время, необходимое для выполнения всех ваших задач. Если это вызов веб-службы или вызов БД, который вы выполняете в этих исполняемых файлах, то нелегко не предположить какое-то фиксированное время для них, поскольку оно может варьироваться в зависимости от трафика вашего сайта. Поэтому вместо того, чтобы просто предполагать, что он всегда завершится через какое-то определенное время, вам просто нужно дождаться завершения всех этих исполняемых файлов, прежде чем вы начнете использовать эти результаты.
Если вы не можете использовать CompletableFuture
из Java8 и если вам нужно подождать, пока все ваши runnables, представленные в пуле, будут выполнены, то вы можете использовать invokeAll
на ExecutorService
как ниже.
invokeAll
будет блокировать основной поток, пока все представленные вызовы не будут выполнены.
// removing try-catch blocks for brevity
ExecutorService service = Executors.newFixedThreadPool(2);
List<Callable<Integer>> tasks = new ArrayList<>();
tasks.add(new Callable<Integer>(){
public Integer call(){
Thread.sleep(5000);
System.out.println("thread1 coming out of sleep");
return 1;
}
});
tasks.add(new Callable<Integer>(){
public Integer call(){
Thread.sleep(10000);
System.out.println("thread2 coming out of sleep");
return 2;
}
});
List<Future<Integer>> futures = service.invokeAll(tasks);
System.out.println("back in main");
service.shutdown();
service.awaitTermination(20,TimeUnit.SECONDS);
Выход:
thread1 coming out of sleep
thread2 coming out of sleep
back in main
Другой вариант заключается в использовании ExecutorCompletionService
, Он создан поверх службы исполнителя. это take()
Метод возвращает будущее для задачи, которая завершает следующую, и ожидает, если еще не выполнено ни одной задачи. Чтобы дождаться завершения всех отправленных задач, вы можете вызывать этот метод столько раз, сколько их задач.
ExecutorCompletionService ecs = new ExecutorCompletionService(executorService);
for (int index = 0 ; index < numberOfTasks ; index++) {
ecs.take();
}
Это доступно в Java 6, 7 и 8; может быть там и в предыдущей версии. Подробнее здесь