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

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

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

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, будет вызван, если пользовательский обработчик не был установлен.

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

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

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

Из ага

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. Вставьте ваш код задачи (либо в Runnable, либо в реализацию Callable) с помощью блока кода 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

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

Взято из Javadoc:

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

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

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

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

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

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

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

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

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

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

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

Из Javadoc:

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

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

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

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

Источник

Другие вопросы по тегам