合理使用线程池带来的3个好处:
- 降低资源消耗,重复利用已创建的线程降低线程创建/销毁时造成的消耗;
- 提高响应速度,任务到达时不需等待线程创建即可执行;
- 提高线程的可管理性,无限制地创建线程会造成系统资源的消耗,降低系统的稳定性,使用线程池可以统一分配、调优、监控。
线程池的实现原理


ThreadPoolExecutor执行execute方法分以下4种情况:
- 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁);
- 如果运行的等于或多于corePoolSize,则将任务加入BlockingQueue;
- 如果无法将任务加入BlockingQueue中(队列已满),则创建新的线程执行任务(注意,执行这一步骤需要获取全局锁);
- 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
ThreadPoolExecutor的这种设计思路,是为了避免在执行execute()方法时获取全局锁(这是一个严重的可伸缩瓶颈),在当前运行的线程数大于或等于corePoolSize后,几乎所有的execute()方法调用都是不需要获取全局锁的。
工作线程: 线程池创建线程时,会将线程封装为工作线程Worker,Worker在执行完任务后,会循环获取工作队列中的任务并执行

线程池的使用
线程池的创建
1 | 创建线程池 |
参数说明:
corePoolSize(线程池的基本大小):当提交一个任务到线程池中时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于等于线程池基本大小时就不再创建。另外如果调用了prestartAllCoreThreads()方法,线程池就会提前创建并启动所有基本线程;
runnableTaskQueue(任务队列):用于保存等待执行的任务阻塞队列;
maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大数量,则继续创建新线程执行任务。
ThreadFactory:用于设置创建线程的工厂,可以创建出自定义线程名的线程;
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池已经饱和了,那么这里指定提交新任务后的执行策略。一般由以下四种:
- AbortPolicy: 直接抛出异常;
- CallerRunsPolicy: 只用调用者所在线程来运行任务;
- DiscardOldestPolicy: 丢弃队列里最近的一个任务,并执行当前任务;
- DiscardPolicy: 不处理,直接丢弃。
也可以根据引用场景来实现RejectedExecutionHandler接口自定义策略
向线程池提交任务
两个方法可以提交任务,execute()或submit()方法
- execute()方法没有返回值,无法判断是否被线程池执行成功,输入为Runnable类的实例。
- submit()方法可以返回future类型的值,以此得知是否被执行成功。其中可用get()方法获取返回值,get(long timeout, TimeUnit unit)方法会阻塞当前线程一段时间后立即返回,可能导致任务没被执行完。
关闭线程池
两个方法可以关闭,shutdown()或shutdownNow()。
原理:便利线程池中的工作线程,然后逐个调用线程的interrupt()方法中断线程,所以无法响应中断的任务将无法终止。
shutdown()和shutdownNow()区别:
① shutdownNow()首先将线程池的状态设为STOP,然后尝试停止所有的正在执行或暂停任务线程,并返回等待执行任务的列表。
② shutdown()只将线程池设为SHUTDOWNING状态,然后中断没有正在执行的任务
合理配置线程池
合理配置角度
- 任务性质:CPU密集型任务、IO密集型任务和混合任务;(按照线程池规模划分,CPU密集型:Ncpu+1个线程,IO密集型:2Ncpu,混合型,如果拆分为CPU密集和IO密集的两个任务执行时间相差不大,就可以拆分。*)
- 任务优先级:高、中和低;(使用优先级队列PriorityBlockingQueue处理,其可以让优先级高的任务先执行)
- 任务的执行时间:长、中和短;(交给不同规模的线程池处理,或使用优先级队列,让执行时间短的任务先执行)
- 任务的依赖性:是否依赖其他系统资源。(比如依赖数据库连接的任务,等待时间越长,CPU空闲时间越长,所以应当设置更大的线程数,充分利用CPU资源)
建议使用有界队列:有界队列能增加系统的稳定性和预警能力,可以根据需要设置大一些,如几千。如果某部分出了问题,使用无届队列会导致被阻塞的线程无限量增大。
线程池的监控
监控线程池的属性:
- taskCount: 线程池需要执行的任务数量;
- completedTaskCount: 线程池在运行过程中已经完成的任务数量,小于或等于taskCount;
- largestPoolSize: 线程池里曾经创建过的最大线程数量。通过该属性能知道线程池是否满过,如果该值等于线程池最大线程数,表明曾经满过。
- getPoolSize: 线程池的线程数量。如果线程池不销毁,线程池里的线程不会自动销毁,所以该值只增不减;
- getActiveCount: 获取活动的线程数
通过扩展线程池进行监控。可以通过继承线程池来自定义线程池,重写线程池的beforeExecute、afterExecute和terminated方法,也可以在执行任务前、执行后和线程池关闭前执行一些代码来进行监控