Выберите между отправкой ExecutorService и выполнением ExecutorService

194

Как я должен выбрать между ExecutorService - х представить или выполнить , если возвращаемое значение не моя забота?

Если я тестирую оба, я не вижу никаких различий между ними, кроме возвращаемого значения.

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());
Чеок Ян Ченг
источник

Ответы:

204

Есть разница в обработке исключений / ошибок.

Задача в очереди с , execute()что порождает некоторые Throwableвызовет UncaughtExceptionHandlerдля Threadвыполнения задачи , которая будет вызвано. Значение по умолчанию UncaughtExceptionHandler, в которое обычно выводится Throwableтрассировка стека System.err, будет вызываться, если не установлен специальный обработчик.

С другой стороны, Throwableсгенерированный задачей, поставленной в очередь, submit()будет связывать Throwableс тем, Futureчто было создано в результате вызова submit(). Вызов get()на который Futureбудет бросаться ExecutionExceptionс оригиналом в Throwableкачестве его причин (доступного по телефону getCause()на ExecutionException).

Hochraldo
источник
19
Обратите внимание, что такое поведение не гарантируется, так как оно зависит от того, Runnableзавернуты вы в a Taskили нет, что вы не можете контролировать. Например, если вы Executorна самом деле a ScheduledExecutorService, ваша задача будет внутренне обернута в a, Futureа неперехваченные Throwables будут привязаны к этому объекту.
rxg
4
Я Future, конечно, имею в виду «завернутый или нет». См., Например, Javadoc для ScheduledThreadPoolExecutor # execute .
rxg
61

выполнять : использовать его для огня и забыть о звонках

submit : используйте его, чтобы проверить результат вызова метода и предпринять соответствующие действияFuture возражения, возвращенного вызовом

Из javadocs

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 ()?

  1. Встраивайте код задачи ( запускаемая или вызываемая реализация) с помощью кода блока try {} catch {}
  2. Осуществлять 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
Равиндра бабу
источник
Хорошее четкое объяснение. Хотя на самом деле его расширение НЕ требуется. Только этот будущий объект должен быть использован, чтобы узнать, была ли задача успешной или нет. таким образом, используйте submit (), если вы планируете использовать Future <t>, в противном случае просто используйте execute ()
prash
11

если вам не важен возвращаемый тип, используйте execute. это то же самое, что и submit, только без возврата Future.

Стивен
источник
15
Согласно принятому ответу, это неверно. Обработка исключений - довольно существенная разница.
Zero3
7

Взято из Javadoc:

Метод submitрасширяет базовый метод {@link Executor # execute}, создавая и возвращая {@link Future}, который можно использовать для отмены выполнения и / или ожидания завершения.

Лично я предпочитаю использовать execute, потому что он кажется более декларативным, хотя на самом деле это вопрос личных предпочтений.

Для получения дополнительной информации: в случае ExecutorServiceреализации основная реализация, возвращаемая вызовомExecutors.newSingleThreadedExecutor() - это ThreadPoolExecutor.

Эти submitвызовы предоставляются своим родителем AbstractExecutorServiceи все это требует выполнения внутренне. execute переопределяется / предоставляется ThreadPoolExecutorнапрямую.

Синтаксис
источник
2

Из Javadoc :

Команда может выполняться в новом потоке, в объединенном потоке или в вызывающем потоке по усмотрению реализации Executor.

Таким образом, в зависимости от реализации Executorвы можете обнаружить, что отправляющий поток блокируется во время выполнения задачи.

rxg
источник
1

Полный ответ представляет собой композицию из двух ответов, которые были опубликованы здесь (плюс немного «лишнего»):

  • Отправляя задачу (а не выполняя ее), вы возвращаетесь в будущее, которое можно использовать для получения результата или отмены действия. У вас нет такого контроля, когда вы execute(потому что его идентификатор типа возврата void)
  • executeОжидает, что некоторое Runnableвремя submitможет принимать в качестве аргумента либо a, Runnableлибо a Callable(дополнительную информацию о различиях между ними см. ниже).
  • executeмгновенно всплывает любые непроверенные исключения (он не может генерировать проверенные исключения !!!), в то время как submitпривязывает любое исключение к будущему, которое возвращается в результате, и только когда вы вызываете future.get()(обернутое) исключение, будет сгенерировано. Throwable, который вы получите, является экземпляром, ExecutionExceptionи если вы getCause()вызовете этот объект, он вернет исходный Throwable.

Еще несколько (связанных) моментов:

  • Даже если задача, которую вы хотите выполнить submit, не требует возврата результата, вы все равно можете использовать Callable<Void>(вместо использования Runnable).
  • Отмена задач может производиться с помощью механизма прерывания . Вот пример того, как реализовать политику отмены

Подводя итог, лучше использовать submitс Callable(а не executeс Runnable). Я процитирую книгу Брайана Гетца «Параллелизм в Java на практике»:

6.3.2 Задачи, приносящие результат: требуемые и будущие

Платформа Executor использует Runnable в качестве основного представления задачи. Runnable - довольно ограничивающая абстракция; run не может возвращать значение или вызывать проверенные исключения, хотя он может иметь побочные эффекты, такие как запись в файл журнала или размещение результата в общей структуре данных. Многие задачи представляют собой фактически отложенные вычисления - выполнение запроса к базе данных, получение ресурса по сети или вычисление сложной функции. Для этих типов задач Callable является лучшей абстракцией: он ожидает, что основная точка входа, call, вернет значение, и ожидает, что он может вызвать исключение.7 Executors включает несколько служебных методов для упаковки других типов задач, включая Runnable и java.security.PrivilegedAction с вызываемым объектом.

Нир Альфаси
источник
1

Просто добавив к принятому ответу -

Однако исключения, создаваемые задачами, попадают в обработчик неперехваченных исключений только для задач, отправленных с помощью execute (); для задач, отправленных с помощью submit () в службу исполнителя, любое выброшенное исключение считается частью статуса возврата задачи.

Источник

abhihello123
источник