Разница между executor.submit и executor.execute в этом коде в Java?
Я учусь пользоваться exectorServices
в бассейн threads
и отправить задачи. У меня есть простая программа ниже
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Processor implements Runnable {
private int id;
public Processor(int id) {
this.id = id;
}
public void run() {
System.out.println("Starting: " + id);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("sorry, being interupted, good bye!");
System.out.println("Interrupted "+Thread.currentThread().getName());
e.printStackTrace();
}
System.out.println("Completed: " + id);
}
}
public class ExecutorExample {
public static void main(String[] args) {
Boolean isCompleted=false;
ExecutorService executor = Executors.newFixedThreadPool(2);
for(int i=0; i<5; i++) {
executor.execute(new Processor(i));
}
//executor does not accept any more tasks but the submitted tasks continue
executor.shutdown();
System.out.println("All tasks submitted.");
try {
//wait for the exectutor to terminate normally, which will return true
//if timeout happens, returns false, but this does NOT interrupt the threads
isCompleted=executor.awaitTermination(100, TimeUnit.SECONDS);
//this will interrupt thread it manages. catch the interrupted exception in the threads
//If not, threads will run forever and executor will never be able to shutdown.
executor.shutdownNow();
} catch (InterruptedException e) {
}
if (isCompleted){
System.out.println("All tasks completed.");
}
else {
System.out.println("Timeout "+Thread.currentThread().getName());
}
}
}
Он не делает ничего особенного, но создает два threads
и подает 5 заданий в общей сложности. После каждого thread
завершает свою задачу, она занимает следующую, в коде выше, я использую executor.submit
, Я также изменился на executor.execute
, Но я не вижу разницы в выводе. Каким образом submit and execute
методы разные? Это что то API
говорит
Метод submit расширяет базовый метод Executor.execute(java.lang.Runnable), создавая и возвращая Future, который можно использовать для отмены выполнения и / или ожидания завершения. Методы invokeAny и invokeAll выполняют наиболее часто используемые формы массового выполнения, выполняя набор задач, а затем ожидая завершения хотя бы одной или всех задач. (Класс ExecutorCompletionService можно использовать для написания пользовательских вариантов этих методов.)
Но мне не ясно, что именно это означает? Спасибо
9 ответов
Как вы видите из JavaDoc execute(Runnable)
ничего не возвращает.
Тем не мение, submit(Callable<T>)
возвращает Future
объект, который позволяет вам программно отменить запущенный поток позже, а также получить T
что возвращается, когда Callable
завершается. Смотрите JavaDoc of Future для более подробной информации.
Future<?> future = executor.submit(longRunningJob);
...
//long running job is taking too long
future.cancel(true);
Более того, если future.get() == null
и не выдает никаких исключений, тогда Runnable успешно выполнен
Разница в том, что execute
просто запускает задачу без лишних слов, тогда как submit
возвращает Future
объект для управления задачей. Вы можете сделать следующие вещи с Future
объект:
- Отмените задачу преждевременно, с
cancel
метод. - Подождите, пока задача завершится, с
get
,
Future
интерфейс более полезен, если вы отправляете Callable
в бассейн. Возвращаемое значение call
метод будет возвращен при вызове Future.get
, Если вы не поддерживаете ссылку на Future
, нет никакой разницы.
execute:
Используйте это для огня и забудьте звонки
submit:
Используйте его для проверки результата вызова метода и выполнения соответствующих действий Future
возраженный возвращается по вызову
Основное отличие: Exception
обращение
submit()
скрывается необработанным Exception
в самой структуре.
execute()
бросает без обработки Exception
,
Решение для обработки исключений с submit()
Оберните
Callable or Runnable code in try{} catch{} block
ИЛИ ЖЕ
Держать
future.get() call in try{} catch{} block
ИЛИ ЖЕ
реализовать свой собственный
ThreadPoolExecutor
и переопределитьafterExecute
метод
Относительно тура другие вопросы на
Выполняет заданные задачи, возвращая список Фьючерсов с их статусом и результатами, когда все завершено или истекло время ожидания, в зависимости от того, что произойдет раньше.
Выполняет заданные задачи, возвращая результат одного успешно выполненного задания (т. Е. Без исключения), если оно выполнено до истечения заданного времени ожидания.
использование invokeAll
если вы хотите дождаться завершения всех представленных задач.
использование invokeAny
если вы ищете для успешного завершения одной задачи из N представленных задач. В этом случае выполняемые задачи будут отменены, если одна из задач завершится успешно.
Связанный пост с примером кода:
Выберите между отправкой ExecutorService и выполнением ExecutorService
Основное различие между методами submit () и execute () состоит в том, что ExecuterService.submit () может возвращать результат вычисления, поскольку он имеет тип возврата Future, но метод execute () не может ничего возвратить, поскольку его тип возврата void. Базовым интерфейсом в среде Executor Java 1.5 является интерфейс Executor, который определяет метод execute (Runnable task), основной целью которого является отделение задачи от ее выполнения.
Любая задача, переданная Исполнителю, может быть выполнена одним и тем же потоком, рабочим потоком из пула потоков или любым другим потоком.
С другой стороны, метод submit () определен в интерфейсе ExecutorService, который является подчиненным интерфейсом Executor, и добавляет функциональность завершения пула потоков вместе с добавлением метода submit (), который может принять задачу Callable и вернуть результат. вычисления.
Сходства между execute () и submit () также:
- Оба метода submit () и execute () используются для отправки задачи в среду Executor для асинхронного выполнения.
- Оба submit () и execute () могут принять задачу Runnable.
- Вы можете получить доступ к submit () и execute () из интерфейса ExecutorService, поскольку он также расширяет интерфейс Executor, который объявляет метод execute ().
Помимо того, что метод submit () может возвращать output, а execute () не может, ниже приведены другие заметные различия между этими двумя ключевыми методами среды Executor Java 5.
- Submit () может принять и Runnable и Callable задачу, но execute () может принять только Runnable задачу.
- Метод submit () объявлен в интерфейсе ExecutorService, а метод execute () объявлен в интерфейсе Executor.
- Возвращаемый тип метода submit () - это объект Future, но возвращаемый тип метода execute () является недействительным.
Если вы проверите исходный код, вы увидите, что submit
это своего рода обертка на execute
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
Submit - возвращает объект Future, который можно использовать для проверки результата отправленной задачи. Может использоваться для отмены или проверки isDone и т. Д.
Выполнить - ничего не возвращает.
В execute(Runnable command)
это реализованный метод из интерфейса Executor
. Это означает, что просто выполните команду и ничего не получите.
ExecutorServic
У е есть свои методы запуска задач: submit
, invokeAny
а также invokeAll
все из которых Callable
экземпляры в качестве их основных целей. Хотя есть методы, имеющиеRunnable
как ввод, фактически Runnable
будет адаптирован к Callable
в методе. ЗачемCallable
? Потому что мы можем получитьFuture<T>
результат после отправки задачи.
Но когда вы трансформируете Runnable
к Callable
, полученный результат - это просто переданное вами значение:
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
Итак, в чем смысл прохождения Runnable
отправить вместо того, чтобы просто получить результат по завершении задачи? Потому что есть метод, в котором есть толькоRunnable
как параметр без особого результата.
Прочтите javadoc Future
:
Если вы хотите использовать Future ради возможности отмены, но не обеспечить полезный результат, вы можете объявить типы формы Future> И вернуть null в результате основной задачи.
Итак, если вы просто хотите выполнить Runnable
задача без возвращаемого значения, вы можете использовать execute()
.
если вы хотите запустить Callable
задача, или
если вы хотите запустить Runnable
задача с указанным результатом в качестве символа завершения, или
если вы хотите запустить задачу и иметь возможность ее отменить,
вы должны использовать submit()
.
В дополнение к предыдущим ответам, т.е.
- execute(..) запускает задачу и забывает о ней
- submit(...) возвращает будущее;
Основное преимущество будущего заключается в том, что вы можете установить тайм-аут. Это может быть очень удобно, если у вас есть исполнитель с ограниченным числом потоков, и ваши выполнения занимают вечность, это не приведет к зависанию процесса.
Пример 1: зависает навсегда и заливает экзекьютор
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i=0; i < 5; i++) {
executor.execute(() -> {
while (true) {
System.out.println("Running...")
Thread.sleep(Long.MAX_VALUE)
}
});
}
Ваш вывод будет (т.е. только 2, и он застревает):
Running...
Running...
С другой стороны, вы можете использовать submit и добавить тайм-аут:
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i=0; i < 5; i++) {
Future future = executor.submit(() -> {
while (true) {
System.out.println("Running...");
Thread.sleep(Long.MAX_VALUE);
}
});
try {
future.get(1, TimeUnit.SECONDS);
} catch (Exception e) {
if (!future.isDone()) {
System.out.println("Oops: " + e.getClass().getSimpleName());
future.cancel(true);
}
}
}
Вывод будет выглядеть так (обратите внимание, что экзекьютор не зависает, но вам нужно вручную отменить будущее):
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
В основном, оба вызова выполняются, если вы хотите в будущем объект, вы должны вызвать здесь метод submit() из документа
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
как вы можете видеть, у java действительно нет никакого способа запустить поток, кроме вызова метода run(), IMO. так как я также обнаружил, что Callable.call()
метод вызывается изнутри run()
метод. следовательно, если объект может быть вызван, он все равно вызовет run()
метод, который вызовет inturn call()
метод из док.
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}