1、重要参数
-
核心线程数(corePoolSize):当线程池的线程都忙碌时,再进来新任务时,由最小线程数扩容到核心线程数 。一般设置为cpu的核数
-
最大线程数 (maximumPoolSize):当核心线程数满了,当队列也满了,再来新任务,就会创建新非核心线程来执行这个新任务,直到线程数达到最大线程数。一般设置为cpu的核数*2
-
存活时间(keepAliveTime):非核心线程 =(maximumPoolSize - corePoolSize ) ,非核心线程闲置下来不干活最多存活时间。当非核心线程超过多长时间不执行任务,处于空闲状态,线程池会将它回收,直到线程数量达到核心线程数。如果设置为0,就代表不回收
-
存活时间单位(unit):线程池中非核心线程保持存活的时间的单位
TimeUnit.DAYS; 天
TimeUnit.HOURS; 小时
TimeUnit.MINUTES; 分钟
TimeUnit.SECONDS; 秒
TimeUnit.MILLISECONDS; 毫秒
TimeUnit.MICROSECONDS; 微秒
TimeUnit.NANOSECONDS; 纳秒
- 线程池等待队列(workQueue):维护着等待执行的Runnable对象。当运行当线程数= corePoolSize时,新的任务会被添加到workQueue中,如果workQueue也满了则尝试用非核心线程执行任务,等待队列应该尽量用有界的。
- threadFactory
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。 - handler
corePoolSize、workQueue、maximumPoolSize都不可用的时候执行的饱和策略
2、使用线程池的注意事项
线程池尽量不要使用的时候再创建,不要在业务逻辑里面去创建线程池,因为这样会在每次调用这个方法的时候,都会创建一个线程池。应该在项目启动的时候,就把线程池创建好。
3、使用场景及示例代码
- 任务来了,直接将任务交给线程池去做,至于成不成功、用不用返回值,不去关注。主要适用于一些更新操作,我不太关注执行结果或返回值。
代码:起一个springboot项目
线程池配置类,将线程池交给spring管理:
@Configuration
public class ThreadPoolConfig {
public static final String NAME_PRE = "test-";
@Bean
public static ExecutorService getExecutor(){
return ExecutorBuilder.create()
.setCorePoolSize(8)
.setMaxPoolSize(16)
.setKeepAliveTime(60, TimeUnit.SECONDS)
.setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
.setWorkQueue(new LinkedBlockingDeque<>(2000))
.setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
.setAllowCoreThreadTimeOut(false)
.build();
}
}
service:
public interface IUserService{
void testThreadPool();
}
service实现类:
@Service
@Slf4j
public class IUserServiceImpl implements IUserService {
//通过Resource注解将ExecutorService 的实现类也就是ThreadPoolConfig 中getExecutor返回的线程池对象。
@Resource
private ExecutorService executorService;
@Override
public void testThreadPool() {
log.info("进来了");
//交给线程池里的线程去执行
CompletableFuture.runAsync(() -> {
try {
log.info("进来了1111");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, executorService);
}
controller:
@RequestMapping("/open/test")
public ResultUtil test(){
iUserService.testThreadPool();
//主线程直接返回结果
return ResultUtil.success("test");
}
通过浏览器调用该接口,不用等待10s, 结果直接返回给浏览器
- 任务来了,将任务交给线程池去执行,等任务都执行完了 ,主线程才能解除阻塞,适用于
-
自己的方法很复杂,需要多次查询数据库
-
需要循环调用http接口查询
-
需要for循环查询数据库
-
代码:
起一个springboot项目
保存执行结果:
@Data
public class QueryBo {
private Boolean query1;
private Boolean query2;
private Boolean query3;
}
线程池配置类,将线程池交给spring管理:
@Configuration
public class ThreadPoolConfig {
public static final String NAME_PRE = "test-";
@Bean
public static ExecutorService getExecutor(){
return ExecutorBuilder.create()
.setCorePoolSize(8)
.setMaxPoolSize(16)
.setKeepAliveTime(60, TimeUnit.SECONDS)
.setHandler(new ThreadPoolExecutor.CallerRunsPolicy())
.setWorkQueue(new LinkedBlockingDeque<>(2000))
.setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix(NAME_PRE).build())
.setAllowCoreThreadTimeOut(false)
.build();
}
}
service:
public interface IUserService{
void testThreadPool();
}
service实现类:
@Service
@Slf4j
public class IUserServiceImpl implements IUserService {
//通过Resource注解将ExecutorService 的实现类也就是ThreadPoolConfig 中getExecutor返回的线程池对象。
@Resource
private ExecutorService executorService;
@Override
public void testThreadPool() {
log.info("进来了");
QueryBo queryBo = new QueryBo();
long start = System.currentTimeMillis();
//将任务交给线程池里的线程去执行
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
Boolean aBoolean=null;
try {
aBoolean=query1();
} catch (InterruptedException e) {
e.printStackTrace();
}
queryBo.setQuery1(aBoolean);
}, executorService);
//交给线程池里的线程去执行
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
Boolean aBoolean=null;
try {
aBoolean=query2();
} catch (InterruptedException e) {
e.printStackTrace();
}
queryBo.setQuery2(aBoolean);
}, executorService);
//交给线程池里的线程去执行
CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
Boolean aBoolean=null;
try {
aBoolean=query3();
} catch (InterruptedException e) {
e.printStackTrace();
}
queryBo.setQuery3(aBoolean);
}, executorService);
//将上面的线程池任务装进数组
CompletableFuture[]completableFutures = Stream.of(future1,future2,future3).collect(Collectors.toList()).toArray(new CompletableFuture[3]);
//当数组里的所有交给线程池的任务都执行完后,才会继续向下执行;相当于阻塞
CompletableFuture.allOf(completableFutures).join();
long end=System.currentTimeMillis()- start;
log.info("查询结果{},时间差{}",queryBo.toString(),end);
}
//任务1
public boolean query1() throws InterruptedException {
log.info("进来了1");
Thread.sleep(1000);
return true;
}
//任务2
public boolean query2() throws InterruptedException {
log.info("进来了2");
Thread.sleep(3000);
return true;
}
//任务3
public boolean query3() throws InterruptedException {
log.info("进来了3");
Thread.sleep(5000);
return true;
}
}
controller:
@RequestMapping("/open/test")
public ResultUtil test(){
iUserService.testThreadPool();
return ResultUtil.success("test");
}
通过浏览器调用该接口,5s后,结果才返回给浏览器
通过日志可以观察到,这三个任务由原来串行化执行9s变为多线程并行执行5s