Выбор между ExecutorService отправить и ExecutorService выполнить
Как я должен выбрать между ExecutorServicesubmit или выполнить, если возвращенное значение не моя забота?
Если я тестирую оба, я не вижу никаких различий между ними, кроме возвращаемого значения.
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
, будет вызван если пользовательский обработчик был установлен.С другой стороны, a
Throwable
генерируется задачей в очереди сsubmit()
привязываютсяThrowable
доFuture
что было производится от звонка доsubmit()
. Звонюget()
наFuture
появитсяExecutionException
С оригиналомThrowable
как его причина (можно называтьgetCause()
наExecutionException
).
выполнить: используйте его для огня и забудьте звонки
submit: используйте его для проверки результата вызова метода и принять соответствующие меры на
Future
возразил возвращенный вызовС javadocs
submit(Callable<T> task)
отправляет задачу возврата значения для выполнения и возвращает будущее представление ожидающих результатов задача.
Future<?> submit(Runnable task)
отправляет выполняемую задачу для выполнения и возвращает будущее, представляющее это задача.
void execute(Runnable command)
выполняет данную команду в какой-то момент в будущем. Команда может выполняться в новом потоке, в объединенном потоке или в вызывающем потоке по усмотрению реализации исполнителя.
вы должны принять меры предосторожности при использовании
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.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)
как обрабатывать эти типы сценариев при использовании отправить()?
- добавьте свой код задачи (либо запускаемая, либо вызываемая реализация) с try{} catch{} block code
- реализовать
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
.The
submit
вызовы предоставляются его родителемAbstractExecutorService
и все вызовы выполняются внутренне. execute переопределяется / предоставляетсяThreadPoolExecutor
напрямую.
С документация:
команда может выполняться в новом потоке, в объединенном потоке или в вызывающем потоке по усмотрению реализации исполнителя.
Так что в зависимости от реализации
Executor
вы можете обнаружить, что отправляющий поток блокируется во время выполнения задачи.
полный ответ представляет собой композицию двух ответов, которые были опубликованы здесь (плюс немного "лишних"):
- отправляя задачу (по сравнению с ее выполнением), вы возвращаете будущее, которое может быть использовано для получения результата или отмены действия. У вас нет такого контроля, когда вы
execute
(потому что его тип возврата idvoid
)execute
ждетRunnable
покаsubmit
можно взять либо aRunnable
илиCallable
в качестве аргумента (для получения дополнительной информации о разница между ними-см. ниже).execute
пузыри вверх любые непроверенные-исключения сразу (он не может бросить проверенные исключения!!!), в то время какsubmit
персонализация любой вид исключения из будущего, которое возвращается в результате, и только при вызовеfuture.get()
a (обернутое) исключение будет выдано . Throwable, который вы получите, является экземпляромExecutionException
и если вы назовете этот объектgetCause()
он вернет оригинал Метательный.еще несколько (связанных) пунктов:
- даже если задача, которую вы хотите
submit
не требует возврата результат, вы все еще можете использоватьCallable<Void>
(вместоRunnable
).- отмена заданий может быть выполнена с помощью отмена механизм. Вот это пример о том, как реализовать политику отмены
подводя итог, это лучшая практика, чтобы использовать
submit
С аCallable
(междуexecute
сRunnable
). И я процитирую из "Java concurrency in practice" Брайана Гетца:6.3.2 задачи, связанные с результатом: вызываемые и будущие
платформа Executor использует Runnable в качестве основного представления задачи. Runnable-это довольно ограничение абстракции; run не может вернуть значение или бросить проверено исключения, хотя это может иметь побочные эффекты, такие как запись в журнал файл или поместить результат в общая структура данных. Многие задачи эффективно отложенные вычисления-выполнение запроса к базе данных, выборка ресурс по сети, или вычисление сложной функции. Для эти типы задач, вызываемые является лучшей абстракцией: он ожидает что главная точка входа, вызов, вернет значение и ожидает что это может вызвать исключение.7 исполнителей включает в себя несколько утилит методы обертывания других типов задач, включая запускаемые и Ява.безопасность.PrivilegedAction, с возможностью вызова.