声明:涉及到隐私问题,故该系列文章不会有关项目详细的代码实现与项目细节,仅用作个人记录。
一. 背景
承接上篇link
现在项目中有以下线程池(starter中部分):
@Bean(name = "threadPoolExecutor")
@ConditionalOnMissingBean({ThreadPoolExecutor.class})
public ThreadPoolExecutor createThreadPoolExecutor() {
// 创建线程池
BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("query-pool-%d")
.daemon(true).build();
ThreadPoolExecutor threadPoolExecutor;
threadPoolExecutor = new ThreadPoolExecutor(/*参数*/);
return threadPoolExecutor;
}
@Bean("chainThreadPoolTaskExecutor")
@ConditionalOnMissingBean({ChainThreadPoolTaskExecutor.class})
public AsyncTaskExecutor taskExecutor() {
log.info("taskExecutor()执行");
ThreadPoolConfigProperties configProperties = properties;
ThreadPoolTaskExecutor executor = new ChainThreadPoolTaskExecutor();
executor.setThreadNamePrefix("Script-Executor");
/*设置其他参数*/
executor.initialize();
return executor;
}
针对上面两个线程池一知半解,通过项目中的代码也没有明确出他们使用的具体的场景。现在结合网上一些资料与代码测试跑一下区别。
二 代码区别
仔细看上面两个线程池,ThreadPoolExecutor是juc包下的,AsyncTaskExecutor与ThreadPoolTaskExecutor是spring的。ThreadPoolExecutor的类间关系如下:

在
ThreadPoolExecutor源代码中,都是基于ThreadPoolExecutor做的封装,所以ThreadPoolExecutor是Spring提供的基于ThreadPoolExecutor的封装的线程池。功能大体相同。这里仍然只研究他们的执行任务上的区别。
三 使用区别与方式
ThreadPoolExecutor的用法就不再累赘。
AsyncTaskExecutor主要是配合@Async使用,达到异步的作用。本来多线程就是异步的一种实现方式,这里的异步当然也是多线程来实现。代码示例如下:
@Component
public class TestAsyncMethod {
@Autowired
private ThreadPoolExecutor threadPoolExecutor;
@Resource(name = "chainThreadPoolTaskExecutor")
private AsyncTaskExecutor asyncTaskExecutor;
public static Random random = new Random();
@Async("chainThreadPoolTaskExecutor")//指定线程池,可能容器中存在其他异步线程池
public void taskOne() throws Exception {
System.out.println("开始执行任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("任务一执行时间:" + (end - start));
System.out.println("taskOne name = " + Thread.currentThread().getName());
}
// 省略 taskTwo() , taskThree()代码 因与taskOne()类似
public void taskFour() throws Exception {
System.out.println("开始执行任务四");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("任务四执行时间:" + (end - start));
System.out.println("taskFour name = " + Thread.currentThread().getName());
}
// 省略 taskFive() , taskSix()代码 因与taskThree()类似
/*************************************************线程中存在返回值*************************************************************************/
@Async("mdcThreadPoolTaskExecutor")
public void taskSeven() throws Exception {
System.out.println("开始执行任务七");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
Future future = threadPoolExecutor.submit(() -> {
return getValue();
});
//阻塞当前线程
System.out.println(future.get());
Future future1 = threadPoolExecutor.submit(() -> {
try {
if ((Integer) future.get() == 1) {
System.out.println("future1得到了future的结果");
}
System.out.println("future1结束");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
long end = System.currentTimeMillis();
System.out.println("任务七执行时间:" + (end - start));
System.out.println("taskSeven name = " + Thread.currentThread().getName());
}
public int getValue() throws InterruptedException {
Thread.sleep(6000);
return 1;
}
}
测试方法与注释
@Resource
private ThreadPoolExecutor threadPoolExecutor;
@Resource(name = "chainThreadPoolTaskExecutor")
private AsyncTaskExecutor asyncTaskExecutor;
/**
* 普通调用
* 在主线程中调用方法
* 4-5-6顺序执行,且均为当前主线程
* 开始执行任务四
* 任务四执行时间:670
* taskFour name = http-nio-8080-exec-1
* 开始执行任务五
* 任务五执行时间:785
* taskFive name = http-nio-8080-exec-1
* 开始执行任务六
* 任务六执行时间:590
* taskSix name = http-nio-8080-exec-1
* test1 name = http-nio-8080-exec-1
*/
public String test1() throws Exception {
testAsyncMethod.taskFour();
testAsyncMethod.taskFive();
testAsyncMethod.taskSix();//主线程等待456执行完成继续
System.out.println("test1 name = " + Thread.currentThread().getName());
return "over";
}
/**
* 在主线程中从线程池中拿出一个线程 执行方法 主线程继续向下执行
* 三个任务为顺序 同步执行 使用的线程为同一个
* test2 name = http-nio-8080-exec-2
* 开始执行任务四
* 任务四执行时间:485
* taskFour name = query-pool-5
* 开始执行任务五
* 任务五执行时间:358
* taskFive name = query-pool-5
* 开始执行任务六
* 任务六执行时间:881
* taskSix name = query-pool-5
*/
public String test2() throws Exception {
threadPoolExecutor.execute(()->{
try {
testAsyncMethod.taskFour();
testAsyncMethod.taskFive();
testAsyncMethod.taskSix();
} catch (Exception e) {
e.printStackTrace();
}
});//主线程继续向下执行 不等待上述线程
System.out.println("test2 name = " + Thread.currentThread().getName());
return "over";
}
/**
* 使三个任务异步执行
* 实现效果为@Async
* test3 name = http-nio-8080-exec-7
* 开始执行任务四
* 开始执行任务五
* 开始执行任务六
* 任务四执行时间:49
* taskFour name = query-pool-4
* 任务六执行时间:510
* taskSix name = query-pool-14
* 任务五执行时间:912
* taskFive name = query-pool-1
*/
public String test3() throws Exception {
threadPoolExecutor.execute(()->{
try {
testAsyncMethod.taskFour();
} catch (Exception e) {
e.printStackTrace();
}
});
threadPoolExecutor.execute(()->{
try {
testAsyncMethod.taskFive();
} catch (Exception e) {
e.printStackTrace();
}
});
threadPoolExecutor.execute(()->{
try {
testAsyncMethod.taskSix();
} catch (Exception e) {
e.printStackTrace();
}
});//主线程不等待上述线程
System.out.println("test3 name = " + Thread.currentThread().getName());
return "over";
}
/**
* 注解@Async使用
* 会开三个线程
* 效果同test3
*/
public String test4() throws Exception {
testAsyncMethod.taskOne();
testAsyncMethod.taskTwo();
testAsyncMethod.taskThree();
System.out.println("test4 name = " + Thread.currentThread().getName());
return "over";
}
/********存在返回值调用*********/
/**
* test5 name = http-nio-8080-exec-3
* 开始执行任务七
* 1
* 任务七执行时间:6004
* taskSeven name = Script-Executor4
* future1得到了future的结果
* future1结束
*/
public String testme5() throws Exception {
testAsyncMethod.taskSeven();//主线程不等待这个线程,继续往下执行
System.out.println("test5 name = " + Thread.currentThread().getName());
return "over";
}
/**
* 阻塞时间小于等于12秒
* @return
* @throws Exception
*/
public String testme6() throws Exception {
long start = System.currentTimeMillis();
//开启一个线程
Future future1=asyncTaskExecutor.submit(()->{
return testAsyncMethod.getValue();
});
//开启一个线程
Future future2=asyncTaskExecutor.submit(()->{
return testAsyncMethod.getValue()+1;
});
//未得到值前,主线程阻塞
System.out.println((Integer) future1.get() + (Integer) future2.get());//3
long end = System.currentTimeMillis();//理论上时间小于12秒 ---测试时间6000~6001 (ms)
return end-start +"";
}
/**
* 不难猜想与验证,如果未被@Async注解的方法被asyncTaskExecutor执行,那么效果与threadPoolExecutor一样。毕竟asyncTaskExecutor内部就是threadPoolExecutor。
*/
上述代码主要要注意注释中的线程名。通过上述代码其实就能分辨两个线程池差别了。
值得注意的是,当用asyncTaskExecutor去执行未被@Async注解的方法,作用与threadPoolExecutor是一样的。
大多情况两种线程池可以混用,没有区别。仅对于多个线程任务调度存在差异。
场景区别:如果业务函数task6()依赖task5()的值做计算,那么推荐使用threadPoolExecutor做同步。如果业务函数结果互不影响,如task5()去存数据库表1,task6()去存数据库表2,task7()去计算值,那么使用AsyncExecutor效果更好。若存在线程返回值,仍会阻塞调用future.get()的当前线程,这就即线程中存在返回值且有依赖关系,如上面代码test7。
四 其他
这篇有点灌水文的感觉了,but这个问题分析与解决的过程还是有点僵硬,所以开篇水文记录一下。下一篇重新温习一下 线程,异步,cpu调度...