一、什么是线程池?
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能
二、线程池的种类
Java通过Executors提供四种线程池,分别为:
1、newFixedThreadPool 指定工作数量的线程池
2、newSingleThreadPool 单线程化线程池 保证所有任务按指定顺序执行
3、newCachedThreadPool 可缓存线程池 查看池中有没有以前建立的线程池 有就直接使用 没有则创建新的线程池
4、newScheduledThreadPool 支持定时及周期性执行
下面是创建定长线程池(FixedThreadPool)的一个例子,严格来说,当使用如下代码创建线程池时,是不符合编程规范的。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
原因在于:(摘自阿里编码规约)
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
三、使用ThreadPoolExecutor创建线程池
1、源码如下
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
2、ThreadPoolExecutor的参数详解(6个):
(1)corePoolSize:核心线程数。如果运行线程数小于corePoolSize,提交新任务时就会新建一个线程来运行;
(2)maximumPoolSize:最大线程数。 如果当前运行线程数大于核心线程数(corePoolSize)并小于最大线程数(maximumPoolSize),只有当等待队列已满的情况下才会新建线程。
(3)keepAliveTime和unit: keepAliveTime 为超过 corePoolSize 线程数量的线程最大空闲时间,unit 为时间单位。
(4)workQueue:等待队列。入队策略:直接传递SynchronousQueue、有界队列 ArrayBlockingQueue、无界队列 LinkedBlockingQueue。
(5)hander拒绝策略: 当线程池已经关闭或达到饱和(最大线程和队列都已满)状态时,新提交的任务将会被拒绝。4种拒绝策略:拒绝时抛出异常、 在 execute 方法的调用线程中运行被拒绝的任务、直接丢弃、 丢弃队列中等待时间最长的任务,并执行当前提交的任务。
3、ThreadPoolExecuto的demo
public static void main(String[] args) {
//核心线程数
int corePoolSize = 3;
//最大线程数
int maximumPoolSize = 6;
//超过 corePoolSize 线程数量的线程最大空闲时间
long keepAliveTime = 2;
//以秒为时间单位
TimeUnit unit = TimeUnit.SECONDS;
//创建工作队列,用于存放提交的等待执行任务
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(2);
ThreadPoolExecutor threadPoolExecutor = null;
try {
//创建线程池
threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
new ThreadPoolExecutor.AbortPolicy());
//循环提交任务
for (int i = 0; i < 8; i++) {
//提交任务的索引
final int index = (i + 1);
threadPoolExecutor.submit(() -> {
//线程打印输出
System.out.println("大家好,我是线程:" + index);
try {
//模拟线程执行时间,10s
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//每个任务提交后休眠500ms再提交下一个任务,用于保证提交顺序
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
4、执行流程
(1)当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
(2)当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
(3)当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
(4)当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
(5)当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
(6)当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
四、SpringBoot自定义线程池
1、 启动类开启异步线程支持
2、创建自定义线程池配置类
@Configuration
public class AsyncConfiguration {
@Bean("taskExecutor")
public Executor taskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//核心线程数
threadPoolTaskExecutor.setCorePoolSize(5);
//最大线程数
threadPoolTaskExecutor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
//任务队列的大小
threadPoolTaskExecutor.setQueueCapacity(1000);
//允许线程的空闲时间/秒
threadPoolTaskExecutor.setKeepAliveSeconds(60);
//线程池名的前缀
threadPoolTaskExecutor.setThreadNamePrefix("taskExecutor-");
/**
* 拒绝处理策略
* CallerRunsPolicy():交由调用方线程运行,比如 main 线程。
* AbortPolicy():直接抛出异常。
* DiscardPolicy():直接丢弃。
* DiscardOldestPolicy():丢弃队列中最老的任务。
*/
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//线程初始化
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
3、测试
业务类:
@Slf4j
@Component
public class AsyncTestImpl{
public static Random random = new Random();
@Async("taskExecutor")
public void doTaskOne() throws Exception {
log.info("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("完成任务一,耗时:" + (end - start) + "毫秒");
}
@Async("taskExecutor")
public void doTaskTwo() throws Exception {
log.info("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("完成任务二,耗时:" + (end - start) + "毫秒");
}
@Async("taskExecutor")
public void doTaskThree() throws Exception {
log.info("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("完成任务三,耗时:" + (end - start) + "毫秒");
}
}
测试类:
@Autowired
private AsyncTestImpl asyncTest;
@Test
public void testAsync() throws Exception {
asyncTest.doTaskOne();
asyncTest.doTaskTwo();
asyncTest.doTaskThree();
asyncTest.doTaskOne();
Thread.currentThread().join();//等上面的三个线程都执行完毕,才执行当前主线程,以确保上面的三个线程都能够执行完毕
}
结果: