Конфигурация Spring Java @Scheduling

Использование @Scheduling для запуска метода в @Scheduled(fixedRate = 10000) и настройка потоков @Scheduling путем реализации SchedulingConfigurer

    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }

если я использовал Thread.sleep или Lock , Executor не создает никаких других потоков, если Thread.sleep не просыпается или блокировка не очищается.

Может кто-нибудь объяснить внутреннюю работу, если у меня размер пула 10, они должны создавать 10 потоков со скоростью 10000 миллисекунд.


person Rohit Sharma    schedule 19.12.2017    source источник


Ответы (2)


В основном такое поведение исходит из реализации ScheduledExecutorService, которая используется внутри Spring. Если вы запустите этот код, вы заметите такое же поведение:

public static void main(String[] args) throws Exception {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
    executor.schedule(() -> {
        System.out.println("Running task in thread " + Thread.currentThread().getId());
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            System.out.println("interrupted while sleeping");
        }
    }, 1000, TimeUnit.MILLISECONDS);
    Thread.sleep(10000);
    executor.shutdownNow();
}

Когда вы отправляете задачу в запланированный пул потоков, она упаковывается с помощью RunnableScheduledFuture, который передается методу delayedExecute. Этот метод добавляет задачу в очередь задач и запускает новый воркер, если текущее количество воркеров меньше corePoolSize. Worker пытается получить задачу из очереди и обработать ее, вызывая метод run. Существует специальная реализация DelayedWorkQueue, которая возвращает задачи, только если они готовы к выполнению. Вот как выглядит run метод RunnableScheduledFuture:

    /**
     * Overrides FutureTask version so as to reset/requeue if periodic.
     */
    public void run() {
        boolean periodic = isPeriodic();
        if (!canRunInCurrentRunState(periodic))
            cancel(false);
        else if (!periodic)
            ScheduledFutureTask.super.run();
        else if (ScheduledFutureTask.super.runAndReset()) {
            setNextRunTime();
            reExecutePeriodic(outerTask);
        }
    }

Как видите, он вызывает реальную логику задачи в runAndReset, вычисляет время следующего выполнения и снова отправляет ту же обновленную задачу в очередь (reExecutePeriodic почти совпадает с schedule). Существует только одна периодическая задача для всех выполнений, которая повторно отправляется снова и снова с обновленным временем после завершения предыдущего выполнения. Таким образом, такой пул потоков запускает только один экземпляр каждого типа задач в любой момент и масштабируется только для разных типов задач.

Если вам интересно, как Spring планирует задачи, взгляните на класс ScheduledTaskRegistrar и, в частности, на метод scheduleFixedDelayTask.

person Nikita Gorbachevski    schedule 20.12.2017

В случае, если вы используете пул потоков: по умолчанию в вашем пуле будет 10 потоков (уже инициализированных). При первом выполнении @scheduled эта функция будет выполняться в потоке из вашего пула (сейчас осталось 9 потоков), если функция еще не завершена и @scheduled выполняется снова, ваша функция будет выполняться в другом потоке от вас. ваш пул, так что теперь у вас осталось 8 потоков в вашем пуле. (8 холостых, 2 запущенных потока)

Если вы не используете пул потоков, используется только один поток.

весенняя документация:

Если вы не укажете атрибут «размер пула», пул потоков по умолчанию будет иметь только один поток. Других параметров настройки планировщика нет.

https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/html/scheduling.html

person Makoton    schedule 19.12.2017
comment
Согласитесь, но я наблюдаю, что если я использовал Thread.sleep или блокировку внутри метода @scheduled для размера пула 10, то поток выполняется только один раз, когда он заблокирован навсегда. Допустим, 1 поток выполняется в запланированном методе из пула из 10, и 1 поток застрял в блокировке, тогда другие 9 потоков не будут выполняться для метода запланированного метода. все застряли. Даже если я использовал Sleep. - person Rohit Sharma; 20.12.2017
comment
Можете ли вы поделиться своим кодом? Потому что похоже, что код использует один поток. - person Makoton; 20.12.2017
comment
только @Async нужно добавить с аннотацией расписания. - person Rohit Sharma; 08.02.2018