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 执行任务。 RunnableScheduledFuture
的 run
方法如下所示:
/**
* 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
方法。
在@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 执行任务。 RunnableScheduledFuture
的 run
方法如下所示:
/**
* 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
方法。