为什么需要线程池
我们知道相比较进程而言,线程是操作系统调度的更细粒度的资源,如果通过大量手动创建、调用线程,显然线程资源的复用,线程如何更好管理等问题就出现了。所以能不能有一种机制既能解决实现线程的复用,以及线程管理呢,这就提出线程池的概念,类比还有像其他基于池化思想的:数据库连接池,Http连接池等
线程池创建多线程
通常使用Executors
工具类创建一个线程池,该类提供多种类型线程池的创建方法,主要分析一下3种
//创建一个包含指定线程数的线程池(单池多线程)
public static ExecutorService newFixedThreadPool(int nThreads)
//创建一个只包含一个工作线程的线程池(单池单线程)
public static ExecutorService newSingleThreadExecutor()
//创建一个能自动扩容的线程池
public static ExecutorService newCachedThreadPool()
查看源码,这三个方法底层本质都是依赖ThreadPoolExecutor
,所以自定义线程池的话,我们重点关注ThreadPoolExecutor
类就行,并且在阿里巴巴手册也推荐使用ThreadPoolExecutor
构建一个线程池,因为它更灵活,可读性更高,适用更多的需求场景。后面会深入理解ThreadPoolExecutor,来了解线程池的整个工作原理
ThreadPoolExecutor线程池工作原理
工作流程:
ThreadPoolExecutor执行execute方法分下面4种情况
1.如果当前运行的线程数小于corePool,直接创建新线程来执行提交的任务
2.如果当前运行的线程数大于或等于corePool,将任务加入阻塞队列
3.如果无法加入任务到阻塞队列(队列已满),创建新的线程来执行任务
4.如果创建的线程数超过了maximumPool,任务将被拒绝,调用RejectedExecutionHandler.rejectedExecution()方法
ThreadPoolExecutor核心参数分析
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize:核心线程数
maximumPool:线程池能够容纳的最大线程数
BlockingQueue:用来暂时保存任务的阻塞队列
keepAliveTime:空闲线程存活时间
unit:时间单位
ThreadFactory:线程工厂
RejectedExecutionHandler:拒绝策略,当任务队列已满并且达到最大线程数后,线程池会调用相应的拒绝策略,可以分为以下几种策略
- AbortPolicy(默认):抛出RejectedExecutionException异常吗,程序终止
- CallerRunsPolicy:不抛弃任务,交给原来的调用者调用
- DiscardOldestPolicy:抛弃队列中排队最久的任务,然后将当前任务添加到队列,再次尝试提交
- DiscardPolicy:默默丢弃任务,不予处理,不抛异常