Как управлять выключением ExecutorService, когда мы позволяем ему внедриться?
Предположим, что я пишу службу, которая нуждается в некоторой службе исполнителя / отдельном потоке. я даю возможность использовать фабричный метод, чтобы не беспокоиться о службе исполнителя, но все же хочу разрешить прохождение существующей службы исполнителя (инъекция зависимостей).
Как я могу управлять для executorService.shutdown()
?
Пример кода:
public class ThingsScheduler {
private final ExecutorService executorService;
public ThingsScheduler(ExecutorService executorService) {
this.executorService = executorService;
}
public static ThingsScheduler createDefaultSingleThreaded() {
return new ThingsScheduler(Executors.newSingleThreadExecutor());
}
public scheduleThing() {
executorService.submit(new SomeTask());
}
// implement Closeable?
// @PreDestory?
// .shutdown() + JavaDoc?
}
Существует несколько проблем
- мы должны иметь возможность отключить внутренне созданный исполнитель, или в лучшем случае обработать его автоматически (Spring @PreDestory, или в худшем случае finalize ())
- нужно не отключения исполнителем, если она под внешним управлением (вводят)
Мы могли бы создать некоторый атрибут, указывающий, если executor создается нашим классом или если он вводится, а затем на finalize/@PreDestroy / shutdown hook мы могли бы закрыть его, но это не кажется мне элегантным.
Может быть, нам следует полностью отказаться от заводского метода и всегда требовать инъекции, подталкивая executor lifecycle управление для клиента?
4 ответа:
Я бы сказал, что решение полностью зависит от вас. Сторонние библиотеки, такие как spring, широко используют специальный атрибут, чтобы понять, кто должен выпустить определенный ресурс в зависимости от его создателя.
mongoInstanceCreated
в SimpleMongoDbFactory,localServer
вSimpleHttpServerJaxWsServiceExporter и т. д. Но они делают это, потому что эти классы создаются только для внешнего использования. Если ваш класс используется только в коде приложения, то вы можете либо ввестиexecutorService
и не заботиться о его выпуске или создать и выпустить его внутри класса, который его использует. Этот выбор зависит от дизайна вашего класса/приложения (работает ли ваш класс с любымexecutorService
, является лиexecutorService
общим и используется другими классами и т. д.). В противном случае я не вижу другого варианта, кроме выделенного флага.
Вы можете создать экземпляр анонимного суб-внутреннего класса из вашей фабрики по умолчанию, как показано ниже. Класс определит метод close / @PreDestroy, который будет вызван вашим контейнером DI. например
public class ThingsScheduler { final ExecutorService executorService; public ThingsScheduler(ExecutorService executorService) { this.executorService = executorService; } /** * assuming you are using this method as factory method to make the returned * bean as managed by your DI container */ public static ThingsScheduler createDefaultSingleThreaded() { return new ThingsScheduler(Executors.newSingleThreadExecutor()) { @PreDestroy public void close() { System.out.println("closing the bean"); executorService.shutdown(); } }; } }
Более "элегантным" решением было бы расширить свой ExecutorService и в нем переопределить метод shutdown (какой бы вы ни выбрали). В случае инъекции вы вернете этот расширенный тип, и он будет иметь свою собственную логику завершения работы. В случае с фабрикой-у вас еще есть оригинальная логика.
Поразмыслив еще немного, я пришел к следующим выводам:
- Не думайте о том, чтобы отключить его, если он вводится-кто-то его создал, кто-то другой будет управлять его жизненным циклом
- A executor factory может быть введен вместо Executor, тогда мы создаем экземпляр с помощью factory и управляем его закрытием самостоятельно, как мы управляем жизненным циклом (и в этом случае применяются ответы от других пользователей)