Выбор между 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, с возможностью вызова.