Какие задачи решает ExecutorService
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Задачи ExecutorService в Java
ExecutorService — это интерфейс в пакете java.util.concurrent, который решает проблему управления потоками и асинхронного выполнения задач. Вместо создания новых потоков вручную, ExecutorService предоставляет пул потоков, который эффективно переиспользует существующие потоки.
Основные задачи ExecutorService
1. Управление жизненным циклом потоков
ExecutorService берёт на себя создание, запуск и завершение потоков:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// Создание пула из 5 потоков
ExecutorService executor = Executors.newFixedThreadPool(5);
// Отправка задач в пул
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
System.out.println("Выполняю задачу в " + Thread.currentThread().getName());
});
}
// Завершение: больше задач приниматься не будут
executor.shutdown();
// Проверка, завершены ли все задачи
if (executor.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("Все задачи завершены");
} else {
System.out.println("Истекло время ожидания");
}
}
}
2. Переиспользование потоков (пулирование)
Вместо создания нового потока для каждой задачи, ExecutorService переиспользует существующие потоки из пула:
// ❌ Плохо: создание нового потока для каждой задачи
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
// долгая операция
}).start();
}
// ✅ Хорошо: переиспользование потоков из пула
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
// долгая операция
});
}
executor.shutdown();
Это снижает overhead на создание потоков и улучшает производительность.
3. Асинхронное выполнение задач
Задачи выполняются асинхронно, неблокируя вызывающий поток:
ExecutorService executor = Executors.newSingleThreadExecutor();
// Отправляем задачу и сразу возвращаемся
Future<String> future = executor.submit(() -> {
try {
Thread.sleep(2000); // долгая операция
return "Результат готов";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "Прервано";
}
});
// Основной поток может продолжить работу
System.out.println("Основной поток работает дальше...");
// Ждём результата
String result = future.get(); // блокирует до завершения задачи
System.out.println(result);
executor.shutdown();
4. Возврат результатов через Future
ExecutorService позволяет получать результаты выполнения задач через интерфейс Future:
ExecutorService executor = Executors.newFixedThreadPool(3);
// submit возвращает Future
Future<Integer> future = executor.submit(() -> {
Thread.sleep(1000);
return 42; // возвращаемый результат
});
try {
// get() блокирует до получения результата
Integer result = future.get();
System.out.println("Результат: " + result);
// Проверка статуса
if (future.isDone()) {
System.out.println("Задача завершена");
}
if (!future.isCancelled()) {
System.out.println("Задача не была отменена");
}
} catch (ExecutionException e) {
System.out.println("Ошибка в задаче: " + e.getCause());
} catch (InterruptedException e) {
System.out.println("Ожидание было прервано");
}
executor.shutdown();
5. Контроль одновременного выполнения
Вы контролируете, сколько потоков одновременно выполняют задачи:
import java.util.concurrent.Executors;
public class ConcurrencyControl {
public static void main(String[] args) {
// Пул из 2 потоков — максимум 2 задачи одновременно
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Задача " + taskId + " начата в " +
Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Задача " + taskId + " завершена");
});
}
executor.shutdown();
}
}
// Вывод:
// Задача 0 начата в pool-1-thread-1
// Задача 1 начата в pool-1-thread-2
// Задача 2 начата в pool-1-thread-1 (после завершения задачи 0)
// ...
6. Обработка исключений и ошибок
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
if (true) {
throw new RuntimeException("Что-то пошло не так!");
}
return 42;
});
try {
Integer result = future.get();
} catch (ExecutionException e) {
// ExecutionException оборачивает исключение из задачи
Throwable cause = e.getCause();
System.out.println("Ошибка в задаче: " + cause.getMessage());
} catch (InterruptedException e) {
System.out.println("Ожидание было прервано");
}
executor.shutdown();
Типы ExecutorService
// 1. Fixed Thread Pool — фиксированное число потоков
ExecutorService fixed = Executors.newFixedThreadPool(10);
// 2. Cached Thread Pool — динамическое число потоков (0 до max)
ExecutorService cached = Executors.newCachedThreadPool();
// 3. Single Thread Executor — один поток
ExecutorService single = Executors.newSingleThreadExecutor();
// 4. Scheduled Executor Service — для повторяющихся задач
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);
scheduled.schedule(() -> System.out.println("Через 2 сек"), 2, TimeUnit.SECONDS);
scheduled.scheduleAtFixedRate(() -> System.out.println("Каждые 5 сек"), 0, 5, TimeUnit.SECONDS);
// 5. Custom ThreadPoolExecutor
ExecutorService custom = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100) // очередь задач
);
7. Batch обработка задач
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
ExecutorService executor = Executors.newFixedThreadPool(3);
// Создание списка задач
List<Callable<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
tasks.add(() -> taskId * 2);
}
// invokeAll выполняет все задачи и возвращает результаты
try {
List<Future<Integer>> futures = executor.invokeAll(tasks);
for (Future<Integer> future : futures) {
System.out.println("Результат: " + future.get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
Правильное завершение ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(5);
// Отправляем задачи
for (int i = 0; i < 20; i++) {
executor.submit(() -> {
// работа
});
}
// Правильное завершение
executor.shutdown(); // принимать новые задачи не будет
try {
// Ждём завершения максимум 60 секунд
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
// Если не завершился за время, принудительно завершаем
List<Runnable> remainingTasks = executor.shutdownNow();
System.out.println("Осталось невыполненных задач: " + remainingTasks.size());
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
Практический пример: параллельная обработка файлов
public class FileProcessing {
public static void main(String[] args) throws Exception {
List<String> files = List.of("file1.txt", "file2.txt", "file3.txt");
ExecutorService executor = Executors.newFixedThreadPool(3);
List<Future<Integer>> futures = new ArrayList<>();
// Обработка каждого файла в отдельном потоке
for (String file : files) {
Future<Integer> future = executor.submit(() -> {
return processFile(file);
});
futures.add(future);
}
// Сбор результатов
int totalProcessed = 0;
for (Future<Integer> future : futures) {
totalProcessed += future.get(); // блокирует до завершения
}
System.out.println("Обработано записей: " + totalProcessed);
executor.shutdown();
}
static int processFile(String filename) {
// Имитация обработки
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return 100;
}
}
Итого: основные задачи ExecutorService
- Управление потоками — создание, переиспользование и завершение
- Пулирование потоков — снижение overhead на создание потоков
- Асинхронное выполнение — неблокирующее выполнение задач
- Получение результатов — через Future API
- Контроль concurrency — управление числом одновременно выполняемых задач
- Обработка ошибок — перехват исключений в задачах
- Graceful shutdown — корректное завершение работы пула
ExecutorService — это предпочтительный способ работы с многопоточностью в Java вместо прямого использования класса Thread.