вводить
С увеличением количества ядер, доступных в современных процессорах, многопоточные API стали очень популярными, поскольку потребность в достижении более высокой пропускной способности продолжает расти. Java предоставляет собственную многопоточную структуру, называемуюПлатформа исполнителя.
1. Что такое инфраструктура Executor?
Инфраструктура Executor содержит набор компонентов для эффективного управления рабочими потоками. API исполнителя черезExecutors
Отделите выполнение задач от реальных задач, которые необходимо выполнить. Этопроизводитель-потребительРеализация шаблона.
java.util.concurrent.Executors
Обеспечивает создание рабочих потоков线程池
заводской метод.
Чтобы использовать инфраструктуру Executor, нам нужно создать пул потоков и отправлять ему задачи на выполнение. Задача платформы Executor заключается в планировании и выполнении отправленных задач и получении возвращенных результатов из пула потоков.
Основная проблема, которая приходит на ум, заключается в том, что когда мы создаемjava.lang.Thread
Объект или вызов реализуетRunnable
/Callable
Зачем вам нужен пул потоков, когда вы взаимодействуете для достижения параллелизма программ?
Ответ исходит из двух основных положений:
- Создание нового потока для новой задачи влечет за собой дополнительные накладные расходы на создание и уничтожение потока. Управление временем жизни этих потоков может значительно увеличить время выполнения ЦП.
- Создание потоков для каждого процесса без каких-либо ограничений приведет к созданию большого количества потоков. Эти потоки потребляют много памяти и вызывают бесполезную трату ресурсов. Когда поток собирается использовать квант времени ЦП после того, как другой поток собирается использовать квант времени ЦП, ЦП будет тратить много времени на переключение контекста потока.
Все эти факторы приводят к снижению пропускной способности системы. Пулы потоков решают эту проблему, сохраняя потоки живыми и повторно используя их. Когда в пул потоков отправляется больше задач, чем выполняется, эти избыточные задачи будут помещены в队列
середина.一旦执行任务的线程有空闲的了,它们会从队列中取下一个任务来执行。对于 JDK 提供的现成的 executors 此任务队列基本是无界的。
2. Типы исполнителей
Теперь, когда мы понимаем, что такое исполнители, давайте рассмотрим различные типы исполнителей.
2.1 SingleThreadExecutor
Этот исполнитель пула потоков имеет только один поток. Он используется для последовательного выполнения задач. Если этот поток зависает из-за исключения при выполнении задачи, для замены этого потока будет создан новый поток, и последующие задачи будут выполняться в новом потоке.
ExecutorService executorService = Executors.newSingleThreadExecutor()
2.2 FixedThreadPool(n)
Как следует из названия, это пул потоков с фиксированным количеством потоков. Задачи, переданные исполнителю, фиксируютсяn
потоки выполняются, если задач больше, они сохраняются вLinkedBlockingQueue
внутри. этот номерn
Обычно общее количество потоков, поддерживаемых базовым процессором.
ExecutorService executorService = Executors.newFixedThreadPool(4);
2.3 CachedThreadPool
Этот пул потоков в основном используется в сценариях, где выполняется большое количество краткосрочных параллельных задач. В отличие от фиксированного пула потоков, этот пул потоков имеет неограниченное количество потоков. Если все потоки заняты выполнением задач и поступает новая задача, этот пул потоков создаст новый поток и отправит его исполнителю. Как только один из потоков становится бездействующим, он выполняет новые задачи. Если поток простаивает в течение 60 секунд, срок его действия истекает, и он удаляется из кеша.
Однако при неправильном управлении или при не очень коротких задачах пул потоков будет содержать большое количество активных потоков. Это может привести к беспорядку ресурсов и, как следствие, снижению производительности.
ExecutorService executorService = Executors.newCachedThreadPool();
2.4 ScheduledExecutor
Этот тип исполнителя используется, когда у нас есть задача, которую необходимо периодически запускать, или когда мы хотим отложить задачу.
ScheduledExecutorService scheduledExecService = Executors.newScheduledThreadPool(1);
можно использоватьscheduleAtFixedRate
илиscheduleWithFixedDelay
существуетScheduledExecutor
Регулярно выполняйте задание.
scheduledExecService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
scheduledExecService.scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit)
Основное различие между этими двумя методами заключается в их реакции на задержку между последовательными выполнениями периодических задач.
scheduleAtFixedRate
: выполнение задачи через равные промежутки времени независимо от того, когда закончилась предыдущая задача.
scheduleWithFixedDelay
: отложенный обратный отсчет начнется только после завершения текущей задачи.
3. Понимание объектов будущего
можно вернуть с помощью executorjava.util.concurrent.Future
Результат объектного доступа к задачам, переданным исполнителю. Будущее можно рассматривать как ответ исполнителя вызывающему.
Future<String> result = executorService.submit(callableTask);
Как упоминалось выше, задача, переданная Исполнителю, является асинхронной, то есть программа не будет ждать завершения текущей задачи, а сразу перейдет к следующему шагу. Вместо этого каждый раз, когда задача выполняется, Executor здесьFuture
установить его в объекте.
Вызывающий может продолжить выполнение основной программы, а когда нужно будет представить результат задачи, он можетFuture
вызов на объект.get()
способ получения. Если задача завершается, результат будет немедленно возвращен вызывающему объекту, в противном случае вызывающий объект будет заблокирован до тех пор, пока исполнитель не завершит выполнение этой операции и не вычислит результат.
Если вызывающая сторона не может бесконечно ждать результата выполнения задачи, это время ожидания также может быть установлено по времени. в состоянии пройтиFuture.get(long timeout,TimeUnit unit)
Реализация метода, выдает, если результат не возвращается в течение указанного диапазона времениTimeoutException
. Вызывающий может обработать это исключение и продолжить выполнение программы.
Если во время выполнения задачи возникает исключение, вызов метода get вызовет исключение.ExecutionException
.
заFuture.get()
Результат, возвращаемый методом, важно то, что реализуется только поставленная задачаjava.util.concurrent.Callable
Интерфейс только возвращаетFuture
. Если задача заключается в достиженииRunnable
интерфейс, то, как только задача будет выполнена, право.get()
Вызов метода вернетnull
.
Другая проблема заключается вFuture.cancel(boolean mayInterruptIfRunning)
метод. Этот метод используется для отмены выполнения отправленной задачи. Если задача уже выполняется, исполнитель попытаетсяmayInterruptIfRunning
отмечен какtrue
Задача прерывания выполняется.
4. Пример: создать и выполнить простой Executor
Теперь мы создадим задачу и попробуем выполнить ее в исполнителе фиксированного пула:
public class Task implements Callable<String> {
private String message;
public Task(String message) {
this.message = message;
}
@Override
public String call() throws Exception {
return "Hello " + message + "!";
}
}
Task
реализация классаCallable
интерфейс и иметьString
Введите как метод, который возвращает значение. Этот метод также может броситьException
. Эта возможность генерировать исключение исполнителю и возможность исполнителя возвращать это исключение вызывающему объекту очень важны, поскольку они помогают вызывающему узнать состояние выполнения задачи.
Теперь давайте выполним эту задачу:
public class ExecutorExample {
public static void main(String[] args) {
Task task = new Task("World");
ExecutorService executorService = Executors.newFixedThreadPool(4);
Future<String> result = executorService.submit(task);
try {
System.out.println(result.get());
} catch (InterruptedException | ExecutionException e) {
System.out.println("Error occured while executing the submitted task");
e.printStackTrace();
}
executorService.shutdown();
}
}
Мы создали количество потоков с 4FixedThreadPool
исполнители, из-за этогоdemo
был разработан на четырехъядерном процессоре. Количество потоков может превышать количество ядер процессора, если выполняемая задача выполняет тяжелые операции ввода-вывода или тратит длительное время на ожидание внешних ресурсов.
мы создаем экземплярTask
класс и передать его исполнителям для выполнения. результатFuture
Объект возвращается, и мы печатаем его на экране.
Давайте работатьExecutorExample
и увидеть его выход:
Hello World!
Как и ожидалось, в квест добавлено приветствиеHello
и черезFuture
объект возвращает результат.
Наконец, мы вызываемexecutorService
shutdown на объекте, чтобы завершить все потоки и вернуть ресурсы в ОС.
.shutdown()
Методы, которые в настоящее время ожидают завершения отправленного исполнителем задания. Однако, если требование немедленно закрывается исполнителем без ожидания, то мы можем использовать.shutdownNow()
метод.
Любые отложенные задачи возвращают результаты вjava.util.List
в объекте.
Мы также можем достичьRunnable
интерфейс для создания той же задачи:
public class Task implements Runnable{
private String message;
public Task(String message) {
this.message = message;
}
public void run() {
System.out.println("Hello " + message + "!");
}
}
Здесь есть несколько важных изменений, когда мы реализуем Runnable.
- Невозможно
run()
Метод получения результатов выполнения задачи. Итак, здесь у нас прямая печать. -
run()
Метод НЕ ДОЛЖЕН генерировать проверенные исключения.
5. Резюме
Поскольку тактовые частоты процессоров изо всех сил пытаются увеличиться, многопоточность становится все более популярной. Однако иметь дело с жизненным циклом каждого потока очень сложно из-за его сложности.
В этой статье мы представили эффективную и простую многопоточную среду Executor Framework и объяснили ее различные компоненты. Мы также рассмотрели различные примеры создания коммитов и выполнения задач в исполнителях.
Как всегда, код для этого примера можно найти по адресуGitHubнайти на.
оригинал:стек abuse.com/concurrency…
автор:Chandan Singh
Переводчик:KeepGoingPawn