线程池
使用好处
- 响应速度快
- 合理利用cpu和内存
- 统一管理线程资源
适用场合
- 服务器大量请求;
- 五个以上就可以使用线程池来管理;
线程池参数
线程池添加线程规则
- 核心线程数没满就创建线程执行任务;
- 如果核心线程数满了,就将新任务存入工作队列;
- 如果核心线程数和工作队列都满了(线程还未到最大线程数),就创建新的线程来执行任务;
- 如果线程达到最大线程数,执行拒绝策略;
线程池希望保持较少的线程数,并且只有在负载很大的情况下才增加它;
KeepAliveTime(最大空闲时间)
如果线程数大于corePoolSize,那么如果多余的线程空闲时间超过KeepAliveTime,就会被回收;
ThreadFactory(线程工厂)
新的线程是由ThreadFactory来创建的,默认使用Executors.defaultThreadFactory,优先级都为5;
WorkQueue(工作队列)
- SynchronousQueue(直接交换,无法存储任务);
- LinkedBlockingQueue(无界队列,如果处理速度跟不上任务提交会报OOM);
- ArrayBlockingQueue(有界队列);
newFixedThreadPool(传入固定线程数)
传入核心线程数;
使用LinkedBlockingQueue,请求堆积占用大量内存,如果处理速度跟不上任务提交会报OOM;
newSingleThreadExecutor(单线程)
核心线程数和最大线程数都是1;
使用LinkedBlockingQueue,请求堆积占用大量内存;
CachedThreadPool(可缓存)
可缓存线程池;
无界线程池,具有自动回收多余线程功能;
核心线程数为0,最大线程数为Integer.MAX_VALUE(如果创建线程非常多会报OOM);
KeepAliveTime为60s,默认回收时间;
使用SynchronousQueue直接交换;
newScheduledThreadPool(支持定时以及周期执行任务)
可以调用一些定时执行任务的方法;
使用DelayedWorkQueue(延迟队列);
workStealingThreadPool(窃取)
子任务(递归场景);
窃取(执行完任务的线程会窃取未执行完任务线程的任务去执行);
线程池数量设置
CPU密集型:线程数=CPU核数+1;
I/O密集型:线程数=CPU核数*(1+平均等待时间/平均工作时间);
常见线程池参数
停止线程池
- shutdown(将已有的任务执行完,新来的任务拒绝);
- isShutdown(返回boolean,是否进入停止状态);
- isTerminated(返回boolean,判断任务是否执行完毕);
- awaitTermination(返回boolean,判断在指定时间内,任务是否执行完毕);
- shutdownNow(立刻关闭线程池,返回工作队列列表List );
拒绝策略
- AbortPolicy(抛出异常);
- DiscardPolicy(丢弃);
- DiscardOldestPolicy(丢弃最老的任务);
- CallerRunsPolicy(提交任务线程去执行);
钩子方法(线程池执行前后调用)
重写beforeExecute方法;
线程池组成部分
- 线程池管理器
- 工作线程
- 任务队列
- 任务Task
Executor(Interface)——ExecutorService(Interface)——AbstractExectorService——ThreadPoolExecutor
Executors(工具类,可以创建线程池);
线程复用(相同线程执行不同的任务)
如果当前线程数<核心线程数,那么就addWork,从队列中取task,然后执行runWorker,调用task.run执行任务;
线程池状态(5种)
RUNNING:接受新任务,处理队列任务;
SHUTDOWN:不接受新任务,处理队列任务;
STOP:不接受新任务,也不处理队列任务,中断正在执行的任务;
TIDYING:所有任务都已终止,运行terminate*钩子方法;
TERMINATED:线程池不能重新启动;
execute源码
//线程池执行线程 传入runnable
public void execute(Runnable command) {
//如果传入线程为null 抛出异常
if (command == null)
throw new NullPointerException();
//取到线程数
int c = ctl.get();
//线程数<corepoolsize
if (workerCountOf(c) < corePoolSize) {
//添加新线程执行任务 传入true判断增加线程数是否小于corePoolSize
if (addWorker(command, true))
return;
c = ctl.get();
}
//线程池是运行状态,线程数>corePoolSize 并且队列没满
if (isRunning(c) && workQueue.offer(command)) {
//检查线程池状态
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
//没有运行就拒绝策略
reject(command);
//检查线程池线程数量,如果是0,就创建新线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//已经达到maxPoolSize了
else if (!addWorker(command, false))
//拒绝策略
reject(command);
}
线程池使用Tips:
- 避免任务堆积;
- 避免线程过度增加;
- 排除线程泄露(执行完毕,线程无法回收);