Обработка задач в Java без блокировки: исполнители и Future
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
Хотите получать уведомления о завершении задач в ExecutorService без потери работы основного потока? Воспользуйтесь колбэками из CompletableFuture. Используйте thenRun
, чтобы добавить колбэк без результатов выполнения задачи, или thenAccept
, чтобы обработать результат выполненной задачи.
ExecutorService executor = Executors.newCachedThreadPool();
CompletableFuture<Integer> futureTask = CompletableFuture.supplyAsync(() -> 42, executor);
futureTask.thenAccept(result -> System.out.println("Результат: " + result));
Здесь выполняется задача, возвращающая 42
, через сервис ExecutorService
. Когда результат становится доступным, функция thenAccept
выведет его, при этом основной поток программы не будет затронут.
Расширение возможностей с помощью CompletableFuture
Асинхронное выполнение задач дает не только возможность получать уведомления, но и гибко управлять запуском и обработкой задач.
Добавление простых колбэков к задачам
Чтобы выполнять дополнительное действие по завершении задачи, можно применить методы thenRun
или thenAccept
.
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// Логика привязанной задачи...
}).thenRun(() -> {
// Логика колбэка, выполняемого после завершения задачи...
});
Работа с колбэками в многозадачности
Методы thenCompose
и thenCombine
удобны для совмещения зависимых задач.
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Привет")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + ", Мир!"));
// "Мир!" появится только после "Привета".
Обработка исключений при работе с колбэками
Непредсказуемые ситуации можно обработать с помощью exceptionally
.
CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> { throw new RuntimeException("Ошибка!"); })
.exceptionally(ex -> 0); // Подменяем исключение на ноль
Мгновенный колбэк по завершении задачи
Настройте ваш метод done()
, используя наследование от FutureTask.
FutureTask<Integer> futureTask = new FutureTask<>(() -> 42) {
@Override
protected void done() {
// Код для обработки окончания выполнения задачи
}
};
executor.execute(futureTask);
Визуализация
Представьте задачи как Гоночную трассу:
ExecutorService 🏁: [Задача 1 🚀, Задача 2 🚀, Задача 3 🚀]
CompletionService 🏎️🏁: [Задача 1 🏆, Задача 2 ❓, Задача 3 ❓]
CompletionService действует в роли наблюдателя, сообщая о завершении каждой задачи.
🚀 Задача 1 завершена: 🏎️🏁🏆 "Задача 1 выполнена!"
🚀 Задача 3 опередила: 🏎️🏁🏆 "Задача 3 стала легендой!"
CompletionService позволяет получать результаты по мере их готовности, не блокируя основной поток программы.
Обогащение инструментария с помощью колбэков
Мощь ListenableFuture от Guava
ListenableFuture и ListeningExecutorService от Guava предоставляют расширенный контроль над асинхронными задачами. Они позволяют регистрировать колбэки.
ListeningExecutorService service = MoreExecutors.listeningDecorator(executor);
ListenableFuture<Integer> future = service.submit(() -> 42);
Futures.addCallback(future, new FutureCallback<Integer>() {
public void onSuccess(Integer result) {
// Код при успешном выполнении
}
public void onFailure(Throwable thrown) {
// Код при возникновении ошибки
}
}, service);
Хуки ThreadPoolExecutor для отслеживания задач
Для получения информации о задачах или выполнения действий перед и после задач используйте хуки beforeExecute и afterExecute.
ThreadPoolExecutor executor = new ThreadPoolExecutor(...) {
@Override
protected void beforeExecute(Thread t, Runnable r) {
// Код перед выполнением задачи
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
// Код после выполнения задачи
}
};
Стабильная производительность с определенным пулом потоков
Для большей уверенности в производительности используйте фиксированный пул потоков.
ExecutorService fixedExecutor = Executors.newFixedThreadPool(10);
Действия после окончания работы ExecutorService
При завершении работы ExecutorService
можно вызвать метод terminated().
ThreadPoolExecutor executor = new ThreadPoolExecutor(...) {
@Override
protected void terminated() {
// Код после окончания работы ExecutorService
}
};
Искусное организование задач и колбэков
Структурируйте асинхронные задачи и колбэки с помощью интерфейса Callable, используйте структуру try-finally для освобождения ресурсов внутри задачи, постобработку результатов выполняйте через thenApply, а при возникновении исключений восстанавливайте цепочку с помощью метода exceptionally.
Полезные материалы
- Callable (Java Platform SE 8 ) – Подробный разбор интерфейса
Callable
. - CompletableFuture (Java Platform SE 8 ) – Проникновение в
CompletableFuture
и усвоение принципов асинхронного программирования. - Executors (Java Platform SE 8 ) – Руководство по фреймворку исполнения в Java и его вспомогательным методам.
- java – How to wait for all threads to finish, using ExecutorService? – Stack Overflow – Советы по управлению завершением потоков с использованием
ExecutorService
. - Shutting down Threads Cleanly – Стильное завершение работы потоков и
ExecutorCompletionService
. - Medium – Начальный курс по основам многопоточности в Java 8.