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


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

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

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

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

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)

как обрабатывать эти типы сценариев при использовании отправить()?

  1. добавьте свой код задачи (либо запускаемая, либо вызываемая реализация) с try{} catch{} block code
  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

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

взято из Javadoc:

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

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

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

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

С документация:

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

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

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

  • отправляя задачу (по сравнению с ее выполнением), вы возвращаете будущее, которое может быть использовано для получения результата или отмены действия. У вас нет такого контроля, когда вы execute (потому что его тип возврата id void)
  • execute ждет Runnable пока submit можно взять либо a Runnable или 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, с возможностью вызова.