Интерфейсы Executor, ExecutorService, ScheduledExecutorService. Пул потоков. Executors
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Пакет java.util.concurrent содержит три интерфейса исполнителей:

Executor – простой интерфейс, поддерживающий запуск новой задачи.

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

ScheduledExecutorService, расширяющий ExecutorService, поддерживает периодическое выполнение задач или их запуск в будущем. Это обощение java.util.Timer, позволяющее создавать пул потоков.

Обычно переменные-ссылки на объекты исполнителей объявляются одного из этих трех интерфейсных типов без указания конкретного имени класса исполнителя.

Интерфейс Executor предоставляет единственный метод, execute(), разработанный для замены общего подхода создания потоков. Если r является Runnable объектом и e является Executor объектом, вы можете заменить
(new Thread(r)).start();
на

e.execute(r);

Однако определение execute() менее специфично. В Java 1.4 создается новый поток и запускается. В зависимости от реализации Executor, execute() может сделать то же самое, но более вероятно использует существующий рабочий поток для запуска r или размещения r в очереди ожидания доступного рабочего потока.

Реализации исполнителей в java.util.concurrent спроектированы так, чтобы в полной мере использовать более продвинутые ExecutorService и ScheduledExecutorService интерфейсы, хотя они также работают с базовым интерфейсом Executor.

Создание нового потока – довольно дорогостоящая (в плане времени и ресурсов) операция, поскольку включает взаимодействие с операционной системой. Если ваша программа создает большое количество кратковременных потоков, то имеет смысл использовать пул потоков. Пул потоков содержит множество простаивающих потоков, готовых к запуску. Вы помещаете Runnable в пул, и один из потоков вызывает его метод run(). Когда метод run() завершается, поток не уничтожается и остается в пуле в готовности обслужить новый запрос.

Другая причина использования пула потоков – необходимость ограничить количество конкурирующих потоков. Создание огромного числа потоков может пагубно отобразиться на производительности. Если у вас есть алгоритм, создающий большое количество потоков, то вам следует установить «фиксированный» пул потоков, который ограничит общее количество конкурирующих потоков. Класс Executors имеет следующие статические методы для конструирования пулов потоков:

newCachedThreadPool () – создаёт пул потоков, выполняющий каждую задачу немедленно, используя существующий простаивающий поток, если он доступен, либо создавая новый в противном случае.

newFixedThreadPool () – создаёт пул потоков фиксированного размера. Если количество задач превышает количество простаивающих потоков, то лишние ставятся в очередь, чтобы быть запущенными, когда завершатся текущие выполняющиеся задачи.

new Single ThreadExecutor () – создает пул размером в 1 поток. Единственый поток выполняет все отправленные ему задачи – одну за другой. Перечисленные три метода возвращают объект класса ThreadPoolExecutor, реализующий интерфейс ExecutorService.

Можно послать Runnable или Callable объекту ExecutorService одним из следующих методов:

Future <?> submit (Runnable task)

Future <T> submit (Runnable task, T result)

Future <T> submit (Callable <T> task)

Пул запустит установленную таким образом задачу при первой возможности. Когда вызывется submit(), вы получаете объект Future, который можно использовать для опроса состояния задачи. Первый метод submit() вернет Future<?>, этот объект можно использовать для вызова isDone(), cancel() или isCancelled(). Однако метод get() по завершении вернёт просто null. Вторая версия submit() также принимает Runnable, а метод get() объекта Future вернет по завершении заданный объект result. Третья версия принимает Callable, а возвращенный Future получает результат вычисления по его готовности.

Завершив работу с пулом потоков, вызывайте shutdown(). Этот метод инициирует последовательность выключения пула. После этого объект пула не принимает новых задач. Когда все задачи завершены, потоки в пуле уничтожаются. В качестве альтернативы можно вызвать shutdownNow(). В этом случае пул отменяет все задачи, которые ещё не стартовали, и пытается прервать работающие потоки. Итак, чтобы использовать пул потоков, надо:

1. Вызвать статический метод newCachedThreadPool() или newFixedThreadPool() класса Executors.

2. Вызвать submit() для установки объектов Runnable или Callable.

3. Если хотите иметь возможность прерывать задачу, или если устанавливаете объекты Callable, обратитесь к возвращенным объектам Future.

4. Вызвать shutdown(), когда больше не собираетесь запускать новых задач.

newScheduledThreadPool ( int threads ) – возвращает пул потоков, использующих заданное их количество для планирования запуска задач.

newSingleThreadScheduledExecutor () – возвращает исполнитель, планирующий запуск задач последовательно в одном потоке.

Эти методы возвращаюь объекы, реализующие интерфейс ScheduledExecutorService.

Пример

class NetworkService implements Runnable {

private final ServerSocket serverSocket;

private final ExecutorService pool;

public NetworkService(int port, int poolSize) throws IOException {

serverSocket = new ServerSocket(port);

pool = Executors.newFixedThreadPool(poolSize);

}

public void run() {

try {

for (;;) {

   pool.execute(new Handler(serverSocket.accept()));

}

} catch (IOException ex) { pool.shutdown(); }

}

}

class Handler implements Runnable {

private final Socket socket;

Handler(Socket socket) { this.socket = socket; }

public void run() { … }

}



Дата: 2019-02-19, просмотров: 231.