Выберите между ExecutorService submit и ExecutorService

Как выбрать ExecutorService submit или execute, если возвращаемое значение не касается меня?

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

От javadocs

submit(Callable<T> task)

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

Future<?> submit(Runnable task)

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

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 {} 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

Ответ 3

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

Ответ 4

Взято из Джавадока:

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

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

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

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

Ответ 5

Из Javadoc:

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

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

Ответ 6

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

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

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

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

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

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

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