В чем преимущества использования ExecutorService?

В чем преимущество использования ExecutorService для выполнения потоков, передающих Runnable в конструктор Thread?

Ответ 1

ExecutorService абстрагирует многие сложности, связанные с абстракциями нижнего уровня, такими как raw Thread. Он обеспечивает механизмы безопасного запуска, закрытия, отправки, выполнения и блокировки при успешном или внезапном завершении задач (выражается как Runnable или Callable).

Из JCiP, раздел 6.2, прямо из устья лошади:

Executor может быть простым интерфейсом, но он создает основу для гибкой и мощной инфраструктуры для асинхронного выполнения задачи, которая поддерживает множество политик выполнения задач. Он представляет собой стандартное средство развязки выполнения задачи из выполнения задачи, описывающее задачи как Runnable. Реализации Executor также обеспечивают поддержку жизненного цикла и перехватчики для сбора статистики, управления приложениями и мониторинга. ... Использование Executor - это, как правило, самый простой путь к реализации дизайна производителя-потребителя в вашем приложении.

Вместо того, чтобы тратить время на выполнение (часто неправильно и с большим трудом) базовую инфраструктуру для parallelism, структура j.u.concurrent позволяет вместо этого сосредоточиться на структурировании задач, зависимостей, потенциальных parallelism. Для большого количества параллельных приложений легко определить и использовать границы задач и использовать j.u.c, что позволит вам сосредоточиться на гораздо меньшем подмножестве настоящих concurrency проблем, которые могут потребовать более специализированных решений.

Кроме того, несмотря на внешний вид шаблона, вопрос о SO спрашивает о хорошей книге, к которой непосредственным ответом является JCiP. Если вы еще этого не сделали, сделайте себе копию. Всесторонний подход к concurrency, представленный там, выходит далеко за рамки этого вопроса и сэкономит вам много душевной боли в долгосрочной перспективе.

Ответ 2

Преимущество, которое я вижу, заключается в управлении/планировании нескольких потоков. С помощью ExecutorService вам не нужно писать свой собственный менеджер потоков, который может быть запутан с ошибками. Это особенно полезно, если ваша программа должна запускать сразу несколько потоков. Например, вы хотите выполнить по два потока за раз, вы можете легко сделать это следующим образом:

ExecutorService exec = Executors.newFixedThreadPool(2);

exec.execute(new Runnable() {
  public void run() {
    System.out.println("Hello world");
  }
});

exec.shutdown();

Пример может быть тривиальным, но попробуйте подумать, что строка "hello world" состоит из тяжелой операции, и вы хотите, чтобы эта операция выполнялась в нескольких потоках за раз, чтобы улучшить производительность вашей программы. Это всего лишь один пример. Есть еще много случаев, когда вы хотите запланировать или запустить несколько потоков и использовать ExecutorService в качестве менеджера потоков.

Для запуска одного потока я не вижу явного преимущества использования ExecutorService.

Ответ 3

Чтобы запустить задачу, реализовав интерфейс Runnable, нам нужно создать объект Thread как new thread(RunnableObject).start(). Но мы знаем, что создание потока имеет свои собственные накладные расходы и хранится в памяти Stack и Heap. Очень дорого создать объект потока только для запуска задачи в отдельном потоке.

Executors framework (java.util.concurrent.Executor), выпущенный Java 5 в пакете java.util.concurrent, используется для запуска объектов Runnable thread без создания объекта Thread.

"Структура Executor - это среда для стандартизации вызова, планирования, выполнения и управления асинхронными задачами в соответствии с набором политик выполнения.

Ответ 4

Ниже приведены некоторые преимущества:

  • Служба-исполнитель управляет потоком асинхронным способом.
  • Использовать вызываемый, чтобы получить результат возврата после завершения потока.
  • Управление распределением работы для свободного потока и перепродажа завершенной работы из потока для автоматического назначения новой работы.
  • fork - структура объединения для параллельной обработки
  • Улучшенная связь между потоками
  • invokeAll и invokeAny дают больше контроля для запуска любого или всего потока сразу
  • shutdown предоставляет возможность для завершения всей работы, связанной с потоком.
  • Запланированные службы-исполнители предоставляют методы для создания повторяющихся вызовов исполняемых файлов и вызываемых вызовов Надеюсь, это поможет вам.

Ответ 5

Следующие ограничения от традиционного потока, преодолеваемого инфраструктурой Executor (встроенная структура пула потоков).

  • Плохое управление ресурсами, т.е. он продолжает создавать новый ресурс для каждого запроса. Нет ограничений на создание ресурса. Используя инфраструктуру Executor, мы можем повторно использовать существующие ресурсы и установить ограничение на создание ресурсов.
  • Не надежно. Если мы продолжим создавать новый поток, мы получим исключение StackOverflowException, следовательно, наша JVM сработает.
  • Накладные расходы Создание времени. Для каждого запроса нам нужно создать новый ресурс. Для создания нового ресурса требуется много времени. т.е. создание темы > задача. Используя инфраструктуру Executor, мы можем построить в пуле потоков.

Преимущества пула потоков

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

  • Использование пула потоков позволяет вам изменить свою политику выполнения по мере необходимости. вы можете перейти из одного потока в несколько потоков, просто заменив реализацию ExecutorService.

  • Пул потоков в приложении Java повышает стабильность системы, создавая настроенное количество потоков, основанное на загрузке системы и доступном ресурсе.

  • Пул потоков освобождает разработчика приложений от материалов управления потоками и позволяет сосредоточиться на бизнес-логике.

Источник

Ответ 6

До версии java 1.5 Thread/Runnable был разработан для двух отдельных сервисов

  • Единица работы
  • Выполнение этой единицы работы

ExecutorService отделяет эти две службы, обозначая Runnable/Callable как единицу работы и Executor как механизм для выполнения (с жизненным циклом) единицы работы

Ответ 7

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

public class TaskOne implements Callable<String> {

@Override
public String call() throws Exception {
    String message = "Task One here. . .";
    return message;
    }
}

public class TaskTwo implements Callable<String> {

@Override
public String call() throws Exception {
    String message = "Task Two here . . . ";
    return message;
    }
}

// from the calling class

ExecutorService service = Executors.newFixedThreadPool(2);
    // set of Callable types
    Set<Callable<String>>callables = new HashSet<Callable<String>>();
    // add tasks to Set
    callables.add(new TaskOne());
    callables.add(new TaskTwo());
    // list of Future<String> types stores the result of invokeAll()
    List<Future<String>>futures = service.invokeAll(callables);
    // iterate through the list and print results from get();
    for(Future<String>future : futures) {
        System.out.println(future.get());
    }

Ответ 8

Неужели так дорого создавать новый поток?

В качестве эталона я создал 60 000 потоков с Runnable с пустым run(). После создания каждого потока я немедленно вызвал его метод start(..). Это заняло около 30 секунд интенсивной работы процессора. Подобные эксперименты были выполнены в ответ на этот вопрос. Резюме состоит в том, что если потоки не заканчиваются сразу, а большое количество активных потоков накапливается (несколько тысяч), тогда будут проблемы: (1) каждый поток имеет стек, поэтому у вас закончится нехватка памяти, (2) может быть ограничение на количество потоков на процесс, наложенный ОС, но не обязательно, кажется.

Итак, насколько я могу судить, если мы говорим о запуске, говорим 10 потоков в секунду, и все они заканчиваются быстрее новых, и мы можем гарантировать, что этот показатель не будет превышен слишком сильно, то ExecutorService не предлагает каких-либо конкретных преимуществ в видимой производительности или стабильности. (Хотя он все же может сделать его более удобным или читаемым для выражения определенных идей concurrency в коде.) С другой стороны, если вы планируете выполнять сотни или тысячи задач в секунду, что требует времени для запуска, вы можете столкнуться с большие проблемы сразу. Это может произойти неожиданно, например. если вы создаете потоки в ответ на запросы на сервер, и есть интенсивность запросов, которые получает ваш сервер. Но, например, один поток в ответ на каждое событие ввода пользователя (нажатие клавиши, движение мыши) кажется совершенно прекрасным, если задачи кратки.