Выберите между отправкой ExecutorService и выполнением ExecutorService - PullRequest
182 голосов
/ 14 октября 2010

Как выбрать между ExecutorService's submit или execute , если возвращаемое значение не является моей проблемой?

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

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

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

Ответы [ 6 ]

195 голосов
/ 21 октября 2010

Существует различие в отношении обработки исключений / ошибок.

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

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

58 голосов
/ 16 февраля 2016

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

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

С Javadocs

submit(Callable<T> task)

Отправляет задачу, возвращающую значение, для выполнения и возвращает будущее представляет ожидающие результаты задачи.

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
11 голосов
/ 14 октября 2010

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

7 голосов
/ 14 октября 2010

взято из Javadoc:

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

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

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

Вызовы submit предоставляются его родителем AbstractExecutorService, и все вызовы выполняются внутри. Выполнение отменяется / предоставляется ThreadPoolExecutor напрямую.

2 голосов
/ 13 июня 2013

Из Javadoc :

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

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

0 голосов
/ 30 апреля 2016

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...