Далее на странице
Executor — интерфейс в пакете java.util.concurrent. Он переданного объекта Runnable, но не предоставляет методов для управления и контроля выполнения задач.
В интерфейсе Executor определён один метод — execute(). При вызове этого метода исполняется поток, то есть метод запускает указанный на исполнение поток.
Java-код. Интерфейс Executor
public interface Executor {
void execute(Runnable command);
}
Интерфейс ExecutorService
Интерфейс ExecutorService - это интерфейс, который расширяет интерфейс Executor и предоставляет дополнительные функции для управления выполнением задач и получения результатов их выполнения.
Java-код. Интерфейс ExecutorService
public interface ExecutorService extends Executor {
void shutdown();
List shutdownNow();
boolean isTerminated();
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
. . .
}
ExecutorService позволяет:
- Управлять выполнением задач, включая запуск, остановку и завершение задач.
- Получать результаты задач, а также обрабатывать их результаты или ошибки.
- Контролировать пул потоков, который может быть настроен для определённых потребностей приложения.
Некоторые методы ExecutorService:
- submit(Callable<T> task). Подает задачу на выполнение и возвращает Future, который предоставляет доступ к результату выполнения задачи и позволяет управлять её состоянием.
- submit(Runnable task). Подает задачу на выполнение и возвращает Future, который связан с переданной задачей, но не возвращает результат.
- shutdown(). Завершает выполнение задач, но даёт возможность уже поставленным задачам завершить выполнение.
- shutdownNow(). Пытается прервать выполнение всех активных задач и возвратить список ожидающих выполнения задач.
ExecutorService - реализация пула Потоков
Интерфейс ExecutorService
пула Потоков.Пул потоков — всегда доступны для выполнения задач.
, которыеПри запуске приложения пул потоков запускает минимальное количество потоков, которые находятся в состоянии ожидания новых задач. Если активных потоков недостаточно для эффективного выполнения задач в пуле, он запускает новые и использует их по принципу повторного использования.
В Java есть встроенный пул потоков — Java ExecutorService, поэтому использовать его можно без необходимости реализовывать самостоятельно.
Пул потоков в Java — это коллекция предварительно инициализированных потоков, которая ожидает заданий и выполняет их. Он позволяет повторно использовать уже созданные потоки для выполнения текущих задач, что устраняет задержку на создание нового потока и делает приложение более отзывчивым.
Пулы потоков часто используются в многопоточных серверах: каждое соединение, приходящее на сервер через сеть, оборачивается в задачу и передаётся в пул потоков, а потоки в нём обрабатывают запросы по соединениям параллельно.
В общем случае пул потоков в Java состоит из:
- пула рабочих потоков, который отвечает за управление потоками;
- фабрики потоков, которая создаёт новые потоки;
- очереди задач, ожидающих выполнения
Пул потоков похож на группу потоков (класс ThreadGroup), но немного другой смысл.
Пул потоков - это набор запущенных автоматически потоков.
Потоки созданы, запущены и они работают всегда.
Создание пула потоков - Класс Executors
Для создания пула потоков в Java можно использовать один из четырёх методов в классе Executors:
- . Создаёт пул из одного потока, одновременно будет выполняться только одна задача.
- . Позволяет создать пул с фиксированным количеством потоков.
- . Создаёт пул, который создаёт новые потоки по запросу и разрушает потоки, которые простаивают больше минуты, если запрос отсутствует.
- . Создаёт пул потоков, который может планировать выполнение задач после заданной задержки или через регулярные промежутки времени.
Класс Executors — это механизм создания пулов потоков и планирования работы асинхронных задач в многопоточном пакете concurrent языка Java.
Он предоставляет методы для создания различных типов служб-исполнителей. С помощью этого класса можно создавать пулы потоков — наборы объектов Runnable и постоянно работающих потоков.
Java-код. Пример 1
public class Executor_1 {
ExecutorService threadPool = Executors.newFixedThreadPool(4);
// Executors.newFixedThreadPool(4) - создаёт в Java поток с фиксированным количеством потоков.
// Метод newFixedThreadPool() принимает один параметр — количество потоков в пуле (в данном случае 4)
}
В любой момент времени при обработке задач будет активно не более четырёх потоков. Если отправить больше задач, чем количество потоков, оставшиеся задачи будут заблокированы до появления свободного потока для их обработки.
Когда подаётся новая задача, служба ExecutorService выбирает один из доступных потоков из пула и выполняет задачу в этом потоке. Если задач больше, чем доступных потоков, и все потоки заняты выполнением существующих задач, то новые задачи ждут своей очереди в очереди.
Метод execute() - выполнение задач в пуле потоков
Метод execute() - подходит для выполнения задач, не требующих отслеживания результата. Он работает с объектами Runnable.
Метод execute() связан с интерфейсом Executor, который используется для выполнения задач в пуле потоков.
Этот метод запускает задачу, заданную объектом Runnable. Не нужно знать о деталях потока, связанного с задачей. Достаточно создать задачу и передать её методу execute() соответствующего исполнителя.
Java-код. Пример 2.1
public class Executor1 {
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(4); // ПУЛ ПОТОКОВ
Task1 task1 = new Task1(); // Объекты Runnable
Task2 task2 = new Task2();
// Метод execute() запускает задачу, заданную объектом Runnable
threadPool.execute(task1); // Запуск выполнения задачи task1 в threadPool.
threadPool.execute(task2);
threadPool.execute(task1);
threadPool.execute(task2);
threadPool.shutdown(); // Остановить все потоки исполнения
}
}
class Task1 implements Runnable { // Задача №1
@Override
public void run() {
System.out.println("Привет: " + Thread.currentThread().getName());
}
}
class Task2 implements Runnable { // Задача №2
@Override
public void run() {
System.out.println("Hello: " + Thread.currentThread().getName());
}
}
Результат в консоли
Hello: pool-1-thread-2
Hello: pool-1-thread-4
Привет: pool-1-thread-1
Привет: pool-1-thread-3
Метод Shutdown() - позволяет останавливать все потоки исполнения, находящиеся под управлением экземпляра ExecutorService. После вызова этого метода ExecutorService больше не принимает новые задания, а все задания, которые раннее были переданы в ExecutorService, продолжат своё выполнение.
Создание объекта Runnable с использованием анонимного класса
Здесь задача создается иначе: объект Runnable создается с использованием анонимного класса.
Java-код. Пример 2.2
public class Executor2 {
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(4); // ПУЛ ПОТОКОВ
Runnable r1 = new Runnable() { // создание объекта Runnable и переопределение run()
@Override
public void run() {
System.out.println("Runnable №1: " + Thread.currentThread().getName());
}
};
Runnable r2 = () -> { // Использование лямбда-выражения
for (int i = 0; i < 5; i++) {
System.out.println("Runnable №2: " + Thread.currentThread().getName());
}
};
Task task = new Task();
threadPool.execute(task); // Запуск выполнения задачи task в threadPool.
threadPool.execute(task);
threadPool.execute(r1); // Запуск выполнения задач r1 и r2
threadPool.execute(r2);
threadPool.shutdown(); // Остановить все потоки исполнения
}
}
class Task implements Runnable { // Задача
@Override
public void run() {
System.out.println("Привет: " + Thread.currentThread().getName());
}
}
Результат в консоли
Привет: pool-1-thread-2
Привет: pool-1-thread-1
Runnable №2: pool-1-thread-4
Runnable №2: pool-1-thread-4
Runnable №2: pool-1-thread-4
Runnable №2: pool-1-thread-4
Runnable №2: pool-1-thread-4
Runnable №1: pool-1-thread-3
Интерфейс Callable в пуле потоков Java позволяет потокам возвращать результаты своей работы. Он похож на интерфейс Runnable, но может вернуть результат в виде объекта Object и способен бросать исключения. Об этом на следующей странице.
P.S.
Добрый день. Я изучаю JAVA. Некоторые темы мне довольно сложно освоить и усвоить. Поэтому я пишу такие вот статья с примерами. Делаю я это в большей степени для себя. Так мне проще понять и запомнить. Если кому-то пригодится – буду рад.
Этот материал используется исключительно в учебных целях и никакой коммерческой выгоды не несет. Текст частично взят из нейросети Яндекса, частично написан мною – материал берется из разных источников. Код писал сам, но за основу опять же брал какие-то уже существующие данные.
Если я где-то ошибся, что-то не правильно/не так описал – это нормально. Во-первых, я учусь, во-вторых, ни на что не претендую. Просьба это понимать.