В чем преимущества использования 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 в коде.) С другой стороны, если вы планируете выполнять сотни или тысячи задач в секунду, что требует времени для запуска, вы можете столкнуться с большие проблемы сразу. Это может произойти неожиданно, например. если вы создаете потоки в ответ на запросы на сервер, и есть интенсивность запросов, которые получает ваш сервер. Но, например, один поток в ответ на каждое событие ввода пользователя (нажатие клавиши, движение мыши) кажется совершенно прекрасным, если задачи кратки.