Платформа параллелизма Java: исполнитель

Java

вводить

С увеличением количества ядер, доступных в современных процессорах, многопоточные API стали очень популярными, поскольку потребность в достижении более высокой пропускной способности продолжает расти. Java предоставляет собственную многопоточную структуру, называемуюПлатформа исполнителя.

1. Что такое инфраструктура Executor?

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

java.util.concurrent.ExecutorsОбеспечивает создание рабочих потоков线程池заводской метод.

Чтобы использовать инфраструктуру Executor, нам нужно создать пул потоков и отправлять ему задачи на выполнение. Задача платформы Executor заключается в планировании и выполнении отправленных задач и получении возвращенных результатов из пула потоков.

Основная проблема, которая приходит на ум, заключается в том, что когда мы создаемjava.lang.ThreadОбъект или вызов реализуетRunnable/CallableЗачем вам нужен пул потоков, когда вы взаимодействуете для достижения параллелизма программ?

Ответ исходит из двух основных положений:

  1. Создание нового потока для новой задачи влечет за собой дополнительные накладные расходы на создание и уничтожение потока. Управление временем жизни этих потоков может значительно увеличить время выполнения ЦП.
  2. Создание потоков для каждого процесса без каких-либо ограничений приведет к созданию большого количества потоков. Эти потоки потребляют много памяти и вызывают бесполезную трату ресурсов. Когда поток собирается использовать квант времени ЦП после того, как другой поток собирается использовать квант времени ЦП, ЦП будет тратить много времени на переключение контекста потока.

Все эти факторы приводят к снижению пропускной способности системы. Пулы потоков решают эту проблему, сохраняя потоки живыми и повторно используя их. Когда в пул потоков отправляется больше задач, чем выполняется, эти избыточные задачи будут помещены в队列середина.一旦执行任务的线程有空闲的了,它们会从队列中取下一个任务来执行。对于 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объект возвращает результат.

Наконец, мы вызываемexecutorServiceshutdown на объекте, чтобы завершить все потоки и вернуть ресурсы в ОС.

.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.

  1. Невозможноrun()Метод получения результатов выполнения задачи. Итак, здесь у нас прямая печать.
  2. run()Метод НЕ ДОЛЖЕН генерировать проверенные исключения.

5. Резюме

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

В этой статье мы представили эффективную и простую многопоточную среду Executor Framework и объяснили ее различные компоненты. Мы также рассмотрели различные примеры создания коммитов и выполнения задач в исполнителях.

Как всегда, код для этого примера можно найти по адресуGitHubнайти на.

оригинал:стек abuse.com/concurrency…

автор:Chandan Singh

Переводчик:KeepGoingPawn