Как гарантировать, что AsyncTaskExecutor корректно очищает виртуальные потоки перед остановкой приложения?

У меня есть собственная реализация AsyncTaskExecutor, которая использует виртуальные потоки для выполнения задач. Я использую здесь ThreadFactory, так как мне нужно завершить задачу для выполнения сквозных задач.

Поскольку VT являются потоками демонов, я думаю, что эти потоки будут продолжать работать в фоновом режиме в течение некоторого времени даже после остановки приложения.

Есть ли способ гарантировать, что все виртуальные потоки, созданные с помощью AsyncTaskExecutor, будут очищены до остановки приложения?

Асинктаскэкзекутор:

public class VirtualThreadTaskExecutor implements AsyncTaskExecutor {
    private final ThreadFactory threadFactory;

    public VirtualThreadTaskExecutor() {
        this.threadFactory = Thread.ofVirtual().name("my-app-virtual-thread-", 0).factory();
    }

    @Override
    public void execute(@NotNull Runnable task) {
        var wrapped = MyTaskWrapper.wrap(task);
        threadFactory.newThread(wrapped).start();
    }

     ...
}

Бин:

@Bean
public AsyncTaskExecutor virtualThreadExecutor() {
    return new VirtualThreadTaskExecutor();
}

Я считаю, что другой вариант — использовать SimpleAsyncTaskExecutor и setTaskTerminationTimeout, которые вызываются при вызове метода close().

Можете ли вы также рассказать, что не так/сложно с setTaskTerminationTimeout в вашем случае?

Naman 13.04.2024 19:00

Ваши рассуждения странны. Почему тот факт, что виртуальные потоки являются потоками-демонами, должен означать, что они будут работать в фоновом режиме дольше, чем потоки платформы? Кроме того, неясно, что вы имеете в виду под «очиститься до остановки приложения». Какую уборку вы имеете в виду?

Holger 16.04.2024 13:31
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
192
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо определения пользовательского VirtualThreadTaskExecutor вы можете использовать TaskDecorator для работы с вашей wrap реализацией и в то же время использовать виртуальные потоки с SimpleAsyncTaskExecutor с setVirtualThreads конфигурацией, установленной как true. Наряду с этим, как вы указали, способ дождаться завершения задачи — это указать его явно с помощью setTaskTerminationTimeout.

@Bean для этого будет выглядеть так:

@Bean
public AsyncTaskExecutor virtualThreadTaskExecutor() {
    SimpleAsyncTaskExecutor asyncTaskExecutor = new SimpleAsyncTaskExecutor();
    asyncTaskExecutor.setVirtualThreads(true); // virtual threads enabled
    asyncTaskExecutor.setTaskDecorator(MyTaskWrapper::wrap); // your custom wrapper
    asyncTaskExecutor.setThreadFactory(Thread.ofVirtual().name("my-app-virtual-thread-", 0).factory());
    asyncTaskExecutor.setTaskTerminationTimeout(5000); // ensure wait for task termination
    return asyncTaskExecutor;
}

Редактировать:

  1. (ссылка: Spring-boot-virtual-threads) Если для spring.threads.virtual.enabled установлено значение true, строка кода setVirtualThreads(true) неявно влияет на поведение методов, помеченных @EnableAsync.

  2. В идеале Spring заботится о базовом жизненном цикле Executor. Но насколько я мог это проверить, вам в основном нужно было бы гарантировать, что завершение задачи учитывается во время завершения работы приложения для такого объявления, путем явного вызова его метода close() через перехватчик, такой как PreDestroy.

setTaskTerminationTimeout появится только после вызова метода close() в asyncTaskExecutor. Означает ли это, что я должен вызвать метод close в хуке жизненного цикла bean-компонента preDestroy?

Govinda Sakhare 13.04.2024 19:47

@GovindaSakhare обновил аспект в ответе, в идеале Spring должен позаботиться о нем неявно, но, насколько я мог проверить (возможно, что-то упустил), вам придется явно вызывать close через крючок.

Naman 13.04.2024 21:14

Другие вопросы по теме