Выберите между отправкой ExecutorService и выполнением ExecutorService
Как выбрать между передачей или выполнением ExecutorService, если возвращаемое значение не является моей проблемой?
Если я тестирую оба, я не вижу различий между ними, кроме возвращаемого значения.
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());
6 ответов
Существует разница в обработке исключений / ошибок.
Задача в очереди с execute()
это порождает Throwable
вызовет UncaughtExceptionHandler
для Thread
выполнение задачи, которая будет вызвана. По умолчанию UncaughtExceptionHandler
, который обычно печатает Throwable
трассировка стека до System.err
, будет вызван, если пользовательский обработчик не был установлен.
С другой стороны, Throwable
генерируется задачей, поставленной в очередь с submit()
будет связывать Throwable
к Future
который был произведен из звонка submit()
, призвание get()
на что Future
бросит ExecutionException
с оригиналом Throwable
как его причина (доступно по вызову getCause()
на ExecutionException
).
выполнить: использовать его для огня и забыть звонки
submit: Используйте его, чтобы проверить результат вызова метода и предпринять соответствующие действия Future
возраженный возвращается по вызову
Из ага
submit(Callable<T> task)
Передает задачу, возвращающую значение, для выполнения и возвращает Future, представляющий ожидающие результаты задачи.
Future<?> submit(Runnable task)
Передает задачу Runnable для выполнения и возвращает Future, представляющий эту задачу.
void execute(Runnable command)
Выполняет данную команду в будущем. Команда может выполняться в новом потоке, в потоке в составе пула или в вызывающем потоке по усмотрению реализации Executor.
Вы должны принять меры предосторожности при использовании submit()
, Он скрывает исключение в самой структуре, если вы не внедрили свой код задачи в try{} catch{}
блок.
Пример кода: этот код глотает Arithmetic exception : / by zero
,
import java.util.concurrent.*;
import java.util.*;
public class ExecuteSubmitDemo{
public ExecuteSubmitDemo()
{
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(10);
//ExtendedExecutor service = new ExtendedExecutor();
service.submit(new Runnable(){
public void run(){
int a=4, b = 0;
System.out.println("a and b="+a+":"+b);
System.out.println("a/b:"+(a/b));
System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
}
});
service.shutdown();
}
public static void main(String args[]){
ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
}
}
выход:
java ExecuteSubmitDemo
creating service
a and b=4:0
Тот же код бросает, заменяя submit()
с execute
():
замещать
service.submit(new Runnable(){
с
service.execute(new Runnable(){
выход:
java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Как обрабатывать сценарии такого типа при использовании submit()?
- Вставьте ваш код задачи (либо в Runnable, либо в реализацию Callable) с помощью блока кода try{} catch{}
- Воплощать в жизнь
CustomThreadPoolExecutor
Новое решение:
import java.util.concurrent.*;
import java.util.*;
public class ExecuteSubmitDemo{
public ExecuteSubmitDemo()
{
System.out.println("creating service");
//ExecutorService service = Executors.newFixedThreadPool(10);
ExtendedExecutor service = new ExtendedExecutor();
service.submit(new Runnable(){
public void run(){
int a=4, b = 0;
System.out.println("a and b="+a+":"+b);
System.out.println("a/b:"+(a/b));
System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
}
});
service.shutdown();
}
public static void main(String args[]){
ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
}
}
class ExtendedExecutor extends ThreadPoolExecutor {
public ExtendedExecutor() {
super(1,1,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
}
// ...
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null)
System.out.println(t);
}
}
выход:
java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
Если вам не важен тип возвращаемого значения, используйте execute. это так же, как представить, только без возвращения будущего.
Взято из Javadoc:
метод
submit
расширяет базовый метод {@link Executor #execute
} создавая и возвращая {@link Future}, который можно использовать для отмены выполнения и / или ожидания завершения.
Лично я предпочитаю использовать execute, потому что он кажется более декларативным, хотя на самом деле это вопрос личных предпочтений.
Чтобы дать больше информации: в случае ExecutorService
реализация, основная реализация возвращается по вызову Executors.newSingleThreadedExecutor()
это ThreadPoolExecutor
,
submit
звонки предоставляются его родителем AbstractExecutorService
и все вызовы выполняются внутри. Выполнение отменено / предоставлено ThreadPoolExecutor
непосредственно.
Полный ответ представляет собой композицию из двух ответов, которые были опубликованы здесь (плюс немного "дополнительно"):
- Отправляя задание (вместо его выполнения), вы получаете будущее, которое можно использовать для получения результата или отмены действия. У вас нет такого контроля, когда вы
execute
(потому что его возвращаемый идентификатор типаvoid
) execute
ожидаетRunnable
в то время какsubmit
может взять либоRunnable
илиCallable
в качестве аргумента (для получения дополнительной информации о разнице между ними - см. ниже).execute
сразу выдувает любые непроверенные исключения (он не может выбрасывать проверенные исключения!!!), в то время какsubmit
связывает любое исключение с будущим, которое возвращается в результате, и только когда вы звонитеfuture.get()
(завернутый) исключение будет сгенерировано. Throwable, который вы получите, является примеромExecutionException
и если вы будете называть этот объектgetCause()
он вернет оригинал Throwable.
Еще несколько (связанных) моментов:
- Даже если задача, которую вы хотите
submit
не требует возврата результата, вы все равно можете использоватьCallable<Void>
(вместо использованияRunnable
). - Отмена задач может быть выполнена с использованием механизма прерывания. Вот пример того, как реализовать политику отмены
Подводя итог, лучше использовать submit
с Callable
(Vs. execute
с Runnable
). И я процитирую из "Параллелизма Java на практике" Брайана Гетца:
6.3.2 Задачи, приносящие результат: вызываемое и будущее
Платформа Executor использует Runnable в качестве базового представления задач. Runnable - довольно ограниченная абстракция; run не может возвращать значение или генерировать отмеченные исключения, хотя может иметь побочные эффекты, такие как запись в файл журнала или помещение результата в общую структуру данных. Многие задачи являются эффективно отложенными вычислениями - выполнение запроса к базе данных, выборка ресурса по сети или вычисление сложной функции. Для этих типов задач Callable - лучшая абстракция: она ожидает, что главная точка входа, call, вернет значение и ожидает, что она может вызвать исключение.7 Executors включает несколько служебных методов для переноса других типов задач, включая Runnable и java.security.PrivilegedAction, с вызываемым.
Из Javadoc:
Команда может выполняться в новом потоке, в потоке в составе пула или в вызывающем потоке по усмотрению реализации Executor.
Так что в зависимости от реализации Executor
Вы можете обнаружить, что отправляющий поток блокируется во время выполнения задачи.
Просто добавив к принятому ответу -
Однако исключения, создаваемые задачами, попадают в обработчик неперехваченных исключений только для задач, отправленных с помощью execute(); для задач, отправленных с помощью submit() в службу исполнителя, любое выброшенное исключение считается частью статуса возврата задачи.