ip6*_*696 6 java spring asynchronous scheduled-tasks threadpool
我写了很长时间的问题,很长。但我试图尽可能多地展示我做了什么,什么是不清楚的。请完成阅读并感谢您的耐心等待!
我尝试了很多实验,写了 spring doc spring doc,(在本站写问题)但还是没看懂全貌。
我有一项任务是在一个 spring-boot 服务器中实现一些调度程序。
South 调度程序必须与线程池一起使用并具有不同的设置。例如第一个 - 5 个线程,第二个 - 10 个线程。虽然我明白了,但我尝试了几个选项,最后还是糊涂了,我应该选择什么以及如何更正确地使用它:
为了测试,我创建了 2 个带有逻辑的 bean,并且每次都会从这个 bean 调用方法:
@Slf4j
@Component
public class TestBean {
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("First bean print");
}
}
Run Code Online (Sandbox Code Playgroud)
和
@Slf4j
@Component
public class TestBean2 {
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Second bean print");
}
}
Run Code Online (Sandbox Code Playgroud)
我仍然不明白其中的区别,使用什么以及何时使用 -@Scheduled
注释或TaskScheduler
代码。我试图用@Scheduled
注释创建一个方法:
@Slf4j
@Component
public class MyScheduler {
private final TestBean testBean;
private final TestBean2 testBean2;
public MyScheduler(TestBean testBean, TestBean2 testBean2) {
this.testBean = testBean;
this.testBean2 = testBean2;
}
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();//call method from first bean every 1 sec
}
}
Run Code Online (Sandbox Code Playgroud)
输出日志:
2018-09-05 13:17:28.799 INFO 10144 --- [pool-1-thread-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:17:37.799 INFO 10144 --- [pool-1-thread-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:17:46.799 INFO 10144 --- [pool-1-thread-1] com.example.scheduling.TestBean : First bean print
Run Code Online (Sandbox Code Playgroud)
工作一个线程并每9 秒从第一个 bean 打印日志。之后我补充说TaskScheduler
:
@Bean
ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
threadPoolTaskScheduler.setThreadNamePrefix("TASK_SCHEDULER_FIRST-");
return threadPoolTaskScheduler;
}
Run Code Online (Sandbox Code Playgroud)
并启动应用程序。输出:
2018-09-05 13:21:40.973 INFO 7172 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:21:49.973 INFO 7172 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:21:58.973 INFO 7172 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:22:07.973 INFO 7172 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
Run Code Online (Sandbox Code Playgroud)
每9 秒但不同的线程从第一个 bean 打印日志。之后,我尝试TaskScheduler
通过另一种方式注入和运行计划:
@Slf4j
@Component
public class MyScheduler {
private final TestBean testBean;
private final TestBean2 testBean2;
private final ThreadPoolTaskScheduler taskScheduler;
public MyScheduler(TestBean testBean, TestBean2 testBean2, ThreadPoolTaskScheduler taskScheduler) {
this.testBean = testBean;
this.testBean2 = testBean2;
this.taskScheduler = taskScheduler;
}
@PostConstruct
public void test() {
taskScheduler.scheduleAtFixedRate(testBean::test, 1000L);
testBean.test();
}
}
Run Code Online (Sandbox Code Playgroud)
但得到了类似的输出:
2018-09-05 13:25:54.541 INFO 7044 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:26:03.541 INFO 7044 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:26:12.541 INFO 7044 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:26:21.541 INFO 7044 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
Run Code Online (Sandbox Code Playgroud)
之后我读到我需要使用@Async
注释并在异步中启动 bean 的方法:
@Slf4j
@Component
public class TestBean {
@Async
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("First bean print");
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
2018-09-05 13:28:07.868 INFO 8608 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:07.868 INFO 8608 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:08.860 INFO 8608 --- [HEDULER_FIRST-4] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:09.860 INFO 8608 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:28:10.860 INFO 8608 --- [HEDULER_FIRST-5] com.example.scheduling.TestBean : First bean print
Run Code Online (Sandbox Code Playgroud)
每1 秒启动一个新线程。就是这样!但是如果我返回@Scheduled
注释怎么办:
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();//async method
}
Run Code Online (Sandbox Code Playgroud)
结果与之前的版本相同。正是需要的!
但现在我想使用第二个豆子。我也将方法设为第二个 bean Async 并尝试启动:
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();
testBean2.test();
}
Run Code Online (Sandbox Code Playgroud)
输出:
2018-09-05 13:32:46.079 INFO 11108 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:32:46.079 INFO 11108 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:32:47.074 INFO 11108 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:32:47.074 INFO 11108 --- [HEDULER_FIRST-4] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:32:48.074 INFO 11108 --- [HEDULER_FIRST-5] com.example.scheduling.TestBean : First bean print
Run Code Online (Sandbox Code Playgroud)
这两种方法都使用 ONEThreadPoolTaskScheduler
和 5 个线程。但我需要用不同的ThreadPoolTaskScheduler
. 我创建第二个ThreadPoolTaskScheduler
:
@Bean
ThreadPoolTaskScheduler taskScheduler2(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(9);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
threadPoolTaskScheduler.setThreadNamePrefix("TASK_SCHEDULER_SECOND-");
return threadPoolTaskScheduler;
}
Run Code Online (Sandbox Code Playgroud)
并开始:
2018-09-05 13:35:31.152 INFO 14544 --- [ main] c.e.scheduling.SchedulingApplication : Started SchedulingApplication in 1.669 seconds (JVM running for 2.141)
2018-09-05 13:35:40.134 INFO 14544 --- [cTaskExecutor-2] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:35:40.134 INFO 14544 --- [cTaskExecutor-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:35:41.127 INFO 14544 --- [cTaskExecutor-4] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:35:41.127 INFO 14544 --- [cTaskExecutor-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:35:42.127 INFO 14544 --- [cTaskExecutor-5] com.example.scheduling.TestBean : First bean print
2018-09-05 13:35:42.127 INFO 14544 --- [cTaskExecutor-6] com.example.scheduling.TestBean2 : Second bean print
Run Code Online (Sandbox Code Playgroud)
两个 bean 都打印日志,但使用cTaskExecutor
和不使用tasckScheduler1
或tasckScheduler2
这是我的第一个问题 - 为什么?它怎么能工作?
现在我尝试使用这个实现:
@Slf4j
@Component
public class MyScheduler {
private final TestBean testBean;
private final TestBean2 testBean2;
private final ThreadPoolTaskScheduler poolTaskScheduler1;
private final ThreadPoolTaskScheduler poolTaskScheduler2;
public MyScheduler(TestBean testBean, TestBean2 testBean2,
@Qualifier("first") ThreadPoolTaskScheduler poolTaskScheduler1,
@Qualifier("second") ThreadPoolTaskScheduler poolTaskScheduler2) {
this.testBean = testBean;
this.testBean2 = testBean2;
this.poolTaskScheduler1 = poolTaskScheduler1;
this.poolTaskScheduler2 = poolTaskScheduler2;
}
// @Scheduled(fixedRate = 1000L)
@PostConstruct
public void test() {
poolTaskScheduler1.scheduleAtFixedRate(testBean::test, 1000L);
poolTaskScheduler2.scheduleAtFixedRate(testBean2::test, 1000L);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:没有任何变化。
在完成后,我恢复了代码:
@Scheduled(fixedRate = 1000L)
public void test() {
testBean.test();
testBean2.test();
}
Run Code Online (Sandbox Code Playgroud)
并@Async
与限定符一起使用:
@Async("first")
@Async("second")
Run Code Online (Sandbox Code Playgroud)
输出:
2018-09-05 13:44:11.489 INFO 7432 --- [EDULER_SECOND-1] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:11.489 INFO 7432 --- [HEDULER_FIRST-1] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:12.484 INFO 7432 --- [EDULER_SECOND-2] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:12.484 INFO 7432 --- [HEDULER_FIRST-2] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:13.484 INFO 7432 --- [HEDULER_FIRST-3] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:13.484 INFO 7432 --- [EDULER_SECOND-3] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:14.484 INFO 7432 --- [EDULER_SECOND-4] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:14.484 INFO 7432 --- [HEDULER_FIRST-4] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:15.484 INFO 7432 --- [EDULER_SECOND-5] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:15.484 INFO 7432 --- [HEDULER_FIRST-5] com.example.scheduling.TestBean : First bean print
2018-09-05 13:44:16.483 INFO 7432 --- [EDULER_SECOND-6] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:17.483 INFO 7432 --- [EDULER_SECOND-7] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:18.483 INFO 7432 --- [EDULER_SECOND-8] com.example.scheduling.TestBean2 : Second bean print
2018-09-05 13:44:19.483 INFO 7432 --- [EDULER_SECOND-9] com.example.scheduling.TestBean2 : Second bean print
Run Code Online (Sandbox Code Playgroud)
正是需要的!但我不明白我是否在做正确的事情
如果我改变ThreadPoolTaskScheduler
对ThreadPoolTaskExecutor
一切的工作方式相同。那我应该用什么?
ThreadPoolTaskScheduler
或ThreadPoolTaskExecutor
@Scheduled
或ThreadPoolTaskScheduler
/ThreadPoolTaskExecutor
来自代码?
@Scheduled
使用ThreadPoolTaskScheduler
/ThreadPoolTaskExecutor
来自代码或@Async
?
让我们一一解答您的问题:
您有自定义的ThreadPoolTaskSchedulers(TaskExecutor Beans),但您没有正确配置@Async。默认情况下,Spring 使用 SimpleAsyncTaskExecutor 来执行您的任务。这就是为什么你只看到带有缩写线程名称[cTaskExecutor-1]、[cTaskExecutor-2]等的日志。如果你想使用taskScheduler1或taskScheduler2,你必须使用相应的名称配置@Async。
@Async("taskScheduler1")
public void test(){
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("First bean print");
}
Run Code Online (Sandbox Code Playgroud)后来,您为您的 bean 配置了执行程序名称“first”和“second”,以便您的异步任务正常工作。@Async 查找并找到具有所提供名称的执行器 Bean。
TaskScheduler 是为调度任务而设计的,TaskExecutor 是为异步任务而设计的。ThreadPoolTaskScheduler 实现了 TaskScheduler 和 TaskExecutor。ThreadPoolTaskExecutor 实现了 TaskExecutor 而不是 TaskScheduler。
您使用调度任务来触发异步任务,因此 ThreadPoolTaskScheduler 和 ThreadPoolTaskExecutor 是可以互换的,除非您想在线程池上使用 ThreadPoolTaskExecutor 的细粒度配置,例如 setCorePoolSize、setMaxPoolSize等。如果您使用超过 1 个调度任务,您想要实现一个ThreadPoolTaskScheduler 因为默认情况下所有 @Scheduled 任务都在 Spring 创建的大小为 1 的默认线程池中执行。