Spring Java @调度配置

Spring Java @Scheduling configuration

在@Scheduled(fixedRate = 10000) 处使用@Scheduling 到运行 方法并通过实施SchedulingConfigurer

设置@Scheduling 线程
    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }

如果我使用 Thread.sleep 或 Lock ,除非 Thread.sleep 唤醒或清除锁,否则 Executor 不会创建其他线程。

有人可以解释一下内部工作原理吗,如果我有 10 个池大小,他们应该以 10000 毫秒的速率创建 10 个线程。

在你使用线程池的情况下: 默认情况下,您的池中将有 10 个线程(已初始化)。第一次执行 @scheduled 时,该函数将在您的池中的一个线程中执行(现在剩余 9 个线程),如果该函数尚未完成并且再次执行 @scheduled,您的函数将在您的其他线程中执行你的池,所以现在你的池中还剩下 8 个线程。 (8 个空闲,2 运行 个线程)

如果您不使用线程池,则只使用一个线程。

spring 文档:

If you do not provide a 'pool-size' attribute, the default thread pool will only have a single thread. There are no other configuration options for the scheduler.

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

基本上这种行为来自 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 实现,只有当它们准备好执行时才 returns 执行任务。 RunnableScheduledFuturerun 方法如下所示:

    /**
     * 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中调用了实际的任务逻辑,计算下一个运行ning时间并再次将相同的更新任务提交到队列中(reExecutePeriodic几乎是与 schedule 相同)。所有执行只有一个周期性任务,在上一次执行完成后一次又一次地重新提交并更新时间。因此,这样的线程池 运行 在任何给定时刻只有每个任务类型的单个实例,并且仅针对不同类型的任务进行扩展。

如果您对 spring 如何安排任务感兴趣,请查看 ScheduledTaskRegistrar class,尤其是 scheduleFixedDelayTask 方法。