Научитесь обрабатывать задачи, отправленные исполнителю и отклоненные из-за того, что исполнитель был по какой-либо причине остановлен, используя RejectedExecutionHandler.
1. Когда задания отклоняются
Помните, когда мы завершаем выполнение исполнителя, мы используем метод shutdown(). Исполнитель ждет завершения задач, которые либо запущены, либо ожидают своего выполнения. Затем он завершает работу исполнителя.
Если мы отправим задачу исполнителю между вызовом метода shutdown() и окончанием его выполнения, то задача будет отклонена. Это происходит потому, что исполнитель больше не принимает новые задачи.
Класс ThreadPoolExecutor предоставляет механизм в виде метода обратного вызова, который вызывается при отклонении задачи.
2. Пример RejectedExecutionHandler
2.1. Создать задачу для выполнения
Давайте создадим демо-задачу, которую мы выполним с помощью фреймворка executor. Это простая задача, которая печатает некое выражение и имитирует случайную задержку, поскольку она делает что-то важное.
class Task implements Runnable{private final String name;public Task(String name) {this.name = name;}@Overridepublic void run() {System.out.printf("%s: Task %s: Created on: %s\n",Thread.currentThread().getName(),name,LocalDateTime.now());try{Long duration =(long)(Math.random()*10);System.out.printf("%s: Task %s: Doing a task during %d seconds\n",Thread.currentThread().getName(),name,duration);TimeUnit.SECONDS.sleep(duration);}catch(InterruptedException e){e.printStackTrace();}System.out.printf("%s: Task %s: Finished on: %s\n",Thread.currentThread().getName(),name,LocalDateTime.now());}@Overridepublic String toString() {return "[name=" + name + "]";}}
2.2. Реализация RejectedExecutionHandler
Создайте класс и реализуйте интерфейс RejectedExecutionHandler. Его метод rejectExecution() отвечает за обработку задач, отклоненных ThreadPoolExecutor.
class RejectedTaskHandler implements RejectedExecutionHandler{@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor){System.out.printf("RejectedTaskHandler: The task %s has been rejected", r.toString());}}
2.3. Добавьте обработчик в executor и протестируйте его
Давайте создадим экземпляр исполнителя и проверим, вызывается ли этот обработчик при отклонении потока. Здесь мы создали кэшированный пул потоков с помощью метода Executors.newFixedThreadPool() для создания исполнителя.
Обратите внимание, что мы использовали метод Runtime.availableProcessors(), который возвращает количество процессоров, доступных JVM. Обычно это число совпадает с количеством ядер компьютера. Изначально пул потоков будет иметь это количество потоков. В моем ноутбуке это 4.
После создания исполнителя мы отправляем задачи типа Runnable на выполнение с помощью метода execute().
По умолчанию, если у исполнителя нет задач для выполнения, он продолжает ждать новых задач и не завершает выполнение. JVM не останавливается после выполнения всех задач. Используйте метод shutdown() класса ThreadPoolExecutor, чтобы указать исполнителю, что мы хотим завершить его выполнение.
После вызова метода shutdown(), если мы попытаемся отправить исполнителю другую задачу, она будет отклонена. По умолчанию исполнитель выдаст исключение RejectedExecutionException. Если мы добавим RejectedExecutionHandler, то исключение не выдается, а вызывается метод-обработчик.
Этот метод вызывается для каждой задачи, отклоненной исполнителем.
import java.time.LocalDateTime;import java.util.concurrent.Executors;import java.util.concurrent.RejectedExecutionHandler;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class Main{public static void main(String[] args){final ThreadPoolExecutor executor =(ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());RejectedTaskHandler handler=new RejectedTaskHandler();executor.setRejectedExecutionHandler(handler);for(int i=0; i<10; i++){Task task=new Task("Task-"+i);executor.execute(task);}//shut down the executor so that new tasks will be rejectedexecutor.shutdown();Task task = new Task("Rejected task");executor.execute(task);}}
Вывод программы.
RejectedTaskHandler: The task [name=Rejected task] has been rejectedpool-1-thread-2: Task Task-1: Created on: 2019-05-22T15:13:51.147pool-1-thread-3: Task Task-2: Created on: 2019-05-22T15:13:51.147pool-1-thread-1: Task Task-0: Created on: 2019-05-22T15:13:51.147pool-1-thread-1: Task Task-0: Doing a task during 6 secondspool-1-thread-4: Task Task-3: Created on: 2019-05-22T15:13:51.147pool-1-thread-4: Task Task-3: Doing a task during 5 secondspool-1-thread-3: Task Task-2: Doing a task during 7 secondspool-1-thread-2: Task Task-1: Doing a task during 4 seconds.........
Очевидно, вызывается метод обработчика RejectedTaskHandler. Здесь мы можем добавить собственную логику для обработки этой задачи в соответствии с требованиями.
Пишите мне свои вопросы в разделе комментариев.