线程池的好处
- 降低资源消耗,避免线程频繁创建和销毁,避免无线创建线程,统一管理线程,通过线程池可以设置核心线程池数量,统一分配线程等,达到线程复用的目的。可以进行线程监控,提高响应速度,减少了创建线程和销毁线程的时间,响应速度就自然提高了
线程池处理流程
-
如果核心线程池和队列已满,创建线程执行任务,是需要获取全局锁的,所以上述步骤的总体思路是为了尽可能避免获取全局锁
-
线程池在创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,会循环获取工作队列里的任务来执行。
-
预热阶段指当前线程<=corePoolSize
线程池的核心参数
-
corePoolSize
-
maximumPoolSize
-
KeepAliveTime:多余空闲线程存活时间,当前线程池的数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。
-
TimeUnit:存活时间单位
-
RejectedExecutionHandler:饱和策略
-
runnableTaskQueue:任务队列
-
ThreadFactory:表示生成线程池中线程的线程工厂,用于创建线程,一般用默认的即可,可以设置创建的线程的名字
-
四种饱和策略:
AbortPolicy : 始终抛出RejectedExecutionException
CallerRunsPolicy : 如果线程池未关闭,则交给调用线程池的线程执行
DiscardPolicy :不处理,丢弃掉
DiscardOldestPolicy : 丢弃队列里最老的任务,然后将当前这个任务重新提交给线程池。
也可以实现RejectedExecutionHandler接口自定义策略
线程池的五种状态
excute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 1. 如果工作线程数小于核心线程数(corePoolSize),则创建一个工作线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 如果当前是running状态,并且任务队列能够添加任务
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 2.1 如果不处于running状态了(使用者可能调用了shutdown方法),则将刚才添加到任务队列的任务移除
if (! isRunning(recheck) && remove(command))
reject(command);
// 2.2 如果当前没有工作线程,则新建一个工作线程来执行任务(任务已经被添加到了任务队列)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 队列已经满了的情况下,则新启动一个工作线程来执行任务
else if (!addWorker(command, false))
reject(command);
}
向线程池提交任务
- execute()
用于提交不需要返回值的任务,无法判断任务是否完成
- submit()
用于提交需要返回值的任务,线程池会返回一个future类型的对象
通过这个future对象可以判断任务是否成功,用future.get()方法会阻塞当前线程直到任务完成,也可以用get(long timeout,TimeUnit unit) 设置阻塞时间
关闭线程池
可以调用线程池的shutdown或者shutdownNow,都是调用线程的interrupt方法中断线程
shutdownNow线程池状态设置为stop,正在执行的任务不一定会执行完,如果shutdown,会把线程池状态设置为shutdown,然后中断所有没有正在执行任务的线程。调用了两种方法其中一种,isShutdown方法会返回true,而所有任务都已关闭后,才表示线程池关闭成功,这时候调用isTerminated方法会返回true
合理配置线程池
根据任务特性,配置线程池,
是CPU密集型任务,还是IO密集型任务,还是混合型,或者说是需要优先级?
CPU密集型应该配置尽可能小的线程,如N+1,N为CPU核数,IO密集型任务可以配置尽可能多的线程,如2*N,混合型,可以进行拆分,看哪个部分占比大,优先级不同的可以使用优先级队列。
(优先级队列,可能会导致低优先级的任务一直得不到执行)
建议任务队列使用有界队列
如果核心线程池满了,无界队列会导致任务一直排队,最后可能会撑爆内存,导致系统崩溃
线程池监控
taskCount:线程池需要执行的任务数量
completedTaskCount:已经完成的任务数量,小于等于taskCount
largestPoolSize:线程池里创建过的最大的线程数量,通过这个数据可以知道线程池是否瞒过
getPoolSize:获得线程池的数量
getActiveCount:获取活动的线程数
任务数量,小于等于taskCount
largestPoolSize:线程池里创建过的最大的线程数量,通过这个数据可以知道线程池是否瞒过
getPoolSize:获得线程池的数量
getActiveCount:获取活动的线程数
还可以使用aop思想,在任务执行前任务执行后,和线程池关闭时,写入一些代码进行监控,比如统计任务执行时间,通过重写beforeExecute、afterExecute、terminated方法。