В чем разница между ExecutorService.submit и ExecutorService.execute в этом коде на Java?

Я учусь использовать ExectorService для объединения threads и отправки задач. У меня есть простая программа ниже

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


class Processor implements Runnable {

    private int id;

    public Processor(int id) {
        this.id = id;
    }

    public void run() {
        System.out.println("Starting: " + id);

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            System.out.println("sorry, being interupted, good bye!");
            System.out.println("Interrupted " + Thread.currentThread().getName());
            e.printStackTrace();
        }

        System.out.println("Completed: " + id);
    }
}


public class ExecutorExample {

    public static void main(String[] args) {
        Boolean isCompleted = false;

        ExecutorService executor = Executors.newFixedThreadPool(2);

        for (int i = 0; i < 5; i++) {
            executor.execute(new Processor(i));
        }

        //executor does not accept any more tasks but the submitted tasks continue
        executor.shutdown();

        System.out.println("All tasks submitted.");

        try {
            //wait for the exectutor to terminate normally, which will return true
            //if timeout happens, returns false, but this does NOT interrupt the threads
            isCompleted = executor.awaitTermination(100, TimeUnit.SECONDS);
            //this will interrupt thread it manages. catch the interrupted exception in the threads
            //If not, threads will run forever and executor will never be able to shutdown.
            executor.shutdownNow();
        } catch (InterruptedException e) {
        }

        if (isCompleted) {
            System.out.println("All tasks completed.");
        } else {
            System.out.println("Timeout " + Thread.currentThread().getName());
        }
    }
}

Он не делает ничего особенного, но создает два threads и отправляет всего 5 задач. После того, как каждый thread завершит свою задачу, он берет следующий. В приведенном выше коде я использую executor.submit. Я тоже поменял на executor.execute. Но я не вижу никакой разницы в выводе. Чем отличаются методы submit и execute? Это то, что говорит API

Метод submit расширяет базовый метод Executor.execute(java.lang.Runnable), создавая и возвращая Future, который можно использовать для отмены выполнения и/или ожидания завершения. Методы invokeAny и invokeAll выполняют наиболее часто используемые формы массового выполнения, выполняя набор задач, а затем ожидая завершения по крайней мере одной или всех задач. (Класс ExecutorCompletionService можно использовать для написания настраиваемых вариантов этих методов.)

Но мне не ясно, что именно это означает?


person brain storm    schedule 10.09.2013    source источник


Ответы (8)


Как видно из JavaDoc, execute(Runnable) ничего не возвращает.

Однако submit(Callable<T>) возвращает объект Future, который позволяет вам позже программно отменить запущенный поток, а также получить T, который возвращается после завершения Callable. Дополнительные сведения см. в разделе JavaDoc будущего.

Future<?> future = executor.submit(longRunningJob);
...
//long running job is taking too long
future.cancel(true);

Более того, если future.get() == null и не выдает никаких исключений, то Runnable выполняется успешно

person dkatzel    schedule 10.09.2013
comment
Просто добавим-> Это не может быть отменено после того, как поток подхватил задачу. Хотя это прервет выполнение потока. - person Shanu Gupta; 03.06.2018
comment
Еще одно отличие состоит в том, что если задание, запущенное с execute(...), выдает исключение, поток будет уничтожен в процессе и, возможно, затем перезапущен ExecutorService. - person Gray; 20.03.2021

Разница в том, что execute просто запускает задачу без лишних слов, тогда как submit возвращает объект Future для управления задачей. Вы можете делать следующие вещи с объектом Future:

  • Преждевременно отмените задачу методом cancel.
  • Дождитесь завершения выполнения задачи с get.

Интерфейс Future более полезен, если вы отправляете Callable в пул. Возвращаемое значение метода call будет возвращено при вызове Future.get. Если вы не поддерживаете ссылку на Future, нет никакой разницы.

person tbodt    schedule 10.09.2013
comment
Нет, в спецификации execute() указано, что данная команда выполняется в какой-то момент в будущем. Таким образом, это не обязательно началось сразу. Разница в том, что вам все равно, когда он выполняется, или проверять такие вещи, как результат или завершение. - person zakmck; 23.01.2021

execute: Используйте его для огня и забудьте о звонках

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

Основное отличие: Exception обработка

submit() скрывает необработанные Exception в самом фреймворке.

execute() бросает необработанный Exception.

Решение для обработки исключений с помощью submit()

  1. Оберните Callable or Runnable code in try{} catch{} block

    OR

  2. Оставить future.get() call in try{} catch{} block

    OR

  3. реализовать свой собственный метод ThreadPoolExecutor и переопределить метод afterExecute

Что касается тура, другие вопросы по

invokeAll< /а>:

Выполняет заданные задачи, возвращая список фьючерсов со своим статусом и результатами, когда все завершено или истечет время ожидания, в зависимости от того, что произойдет раньше.

invokeAny< /а>:

Выполняет заданные задачи, возвращая результат успешного завершения (т. е. без создания исключения), если они выполняются до истечения заданного тайм-аута.

Используйте invokeAll, если хотите дождаться завершения всех отправленных задач.

Используйте invokeAny, если вы ищете успешное выполнение одной задачи из N отправленных задач. В этом случае выполняемые задачи будут отменены, если одна из задач завершится успешно.

Связанный пост с примером кода:

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

person Ravindra babu    schedule 23.08.2016
comment
submit не скрывает исключение, оно выдает его в оболочке ExecutionException при вызове get. Затем исходное исключение может быть получено с помощью ExecutionException.getCause() - person ; 19.03.2019

Основное различие между методами submit() и execute() заключается в том, что ExecuterService.submit() может возвращать результат вычисления, потому что он имеет тип возвращаемого значения Future, но метод execute() не может ничего возвращать, поскольку его тип возвращаемого значения — void. Базовым интерфейсом в среде Executor Java 1.5 является интерфейс Executor, который определяет метод execute(Runnable task), основной целью которого является отделение задачи от ее выполнения.

Любая задача, отправленная в Executor, может быть выполнена тем же потоком, рабочим потоком из пула потоков или любым другим потоком.

С другой стороны, метод submit() определен в интерфейсе ExecutorService, который является подинтерфейсом Executor и добавляет функциональность завершения пула потоков, а также добавляет метод submit(), который может принимать вызываемую задачу и возвращать результат. вычислений.

Сходство между execute() и submit():

  1. Оба метода submit() и execute() используются для отправки задачи в среду Executor для асинхронного выполнения.
  2. И submit(), и execute() могут принимать задачу Runnable.
  3. Вы можете получить доступ к submit() и execute() из интерфейса ExecutorService, потому что он также расширяет интерфейс Executor, который объявляет метод execute().

Помимо того факта, что метод submit() может возвращать выходные данные, а execute() не может, существуют и другие заметные различия между этими двумя ключевыми методами платформы Executor Java 5.

  1. submit() может принимать как Runnable, так и Callable задачу, но execute() может принимать только Runnable задачу.
  2. Метод submit() объявлен в интерфейсе ExecutorService, а метод execute() объявлен в интерфейсе Executor.
  3. Тип возвращаемого значения метода submit() — объект Future, но тип возвращаемого значения метода execute() — void.
person amitkumar12788    schedule 24.01.2019

Submit — возвращает объект Future, который можно использовать для проверки результата отправленной задачи. Может использоваться для отмены или проверки isDone и т. д.

Выполнить - ничего не возвращает.

person Preetham R U    schedule 08.07.2015

Если вы проверите исходный код, вы увидите, что submit является своего рода оболочкой для execute.

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
person Koray Tugay    schedule 04.03.2019

в основном выполняются оба вызова, если вам нужен будущий объект, вы должны вызвать метод submit() здесь из документа

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}


public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

как вы можете видеть, у java действительно нет другого способа запустить поток, кроме вызова метода run(), IMO. так как я также обнаружил, что метод Callable.call() вызывается внутри метода run(). следовательно, если объект вызывается, он все равно вызовет метод run(), который, в свою очередь, вызовет метод call() из doc.

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}
person amarnath harish    schedule 06.07.2018

execute(Runnable command) — это реализованный метод из интерфейса Executor. Это означает, что просто выполните команду и ничего не получите.

ExecutorService имеет свои собственные методы запуска задач: submit, invokeAny и invokeAll, все из которых имеют экземпляры Callable в качестве основных целей. Хотя есть методы, использующие Runnable в качестве входных данных, на самом деле Runnable будет адаптировано к Callable в методе. почему Callable? Потому что мы можем получить результат Future<T> после отправки задачи.

Но когда вы преобразуете Runnable в Callable, результат, который вы получаете, это просто значение, которое вы передаете:

static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

Итак, какой смысл в том, что мы передаем Runnable для отправки вместо того, чтобы просто получить результат, когда задача будет завершена? Потому что есть метод, который имеет только Runnable в качестве параметра без определенного результата.

Прочитайте javadoc Future:

Если вы хотите использовать Future ради отменяемости, но не предоставлять пригодный для использования результат, вы можете объявить типы в форме Future‹?› и вернуть null в результате базовой задачи.

Итак, если вы просто хотите выполнить задачу Runnable без какого-либо возвращаемого значения, вы можете использовать execute().

если вы хотите запустить задачу Callable, или

если вы хотите запустить задачу Runnable с указанным результатом в качестве символа завершения, или

если вы хотите запустить задачу и иметь возможность отменить ее,

вы должны использовать submit().

person Zhang Amos    schedule 21.07.2020