目录
线程池的优点
- 降低资源消耗,通过重复利用已创建的线程降低线程创建销毁的消耗;
- 提高响应速度,任务到达时不需要等到线程创建就能立即执行;
- 调高线程可管理性;
线程池的处理流程
当一个任务进入线程池时,如上图:
- 线程池判断核心线程池的线程是否都在执行任务,如何有空闲线程则使用空闲/新建线程执行任务,如果核心线程满了则进入下一流程
- 判断工作队列是否已满,如果没有则将任务存放在队列中,如果满了则执行下一流程
- 判断最大线程池的线程是否已经达到最大核心线程数,如果没有则新建线程执行任务,如果满了则执行饱和策略
线程池核心参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:线程的基本大小,当任务进入线程池,即使有空闲线程可以执行该任务也会创建一个新的线程,直到达到基本大小,线程池的prestartAllCoreThreads方法可以提前创建启动所有的基本线程.
- maximumPoolSize
- workQueue:用于保存等待执行的任务的阻塞队列,可以选择一下几种
1)ArrayBlockingQueue,基于数组的有界阻塞队列,FIFO存储排序;
2)LinkedBlockingQueue,基于链表的阻塞队列,FIFO存储排序,吞吐量高于ArrayBlockingQueue;newFinxedThreadPool使用了这个队列
3)SynchronousQueue,不存储元素的阻塞队列,每次插入必须等线程调用移除操作,否则插入一直阻塞,吞吐量高于LinkedBlockingQueue;newCachedThreadThreadPool
4)PriorityBlockingQueue,具有优先级的无限阻塞队列 - threadFactory:设置创建线程的工厂 主要为线程设置线程名
- RejectedExecutionHandler
1)AbortPolicy 直接抛异常
2)DiscardPolicy 不处理丢弃掉
3)CallerRunsPolicy 主线程执行
4)DiscardOldestPolicy 丢弃队列里最近的一个任务来执行此任务
关闭线程池
shoutdown/shoutdowNow 原理都是遍历线程调用interrupt方法
区别:shoutdown将线程池设置成SHOUTDOWN状态,然后中断所有没有执行任务的线程;shoutdowNow将线程池设置成STOP状态,尝试停止所有的线程。通常使用shoutdown关闭,如果任务不需要执行完可使用shoutdowNow
合理配置线程池
从以下几个角度分析:
- 任务性质:CPU密集型,IO密集型,混合型
- 任务优先级:高、中、低
- 任务执行时间:长、中、短
- 任务的依赖性:是否依赖其他的系统资源,如数据库连接
CPU密集型应该配置尽可能小的线程N(cpu)+1个线程的线程池;IO密集型任务可能不是一直在执行任务应该配置尽可能多的线程,如N(cpu)*2;混合型如果可以拆分,将其拆分成1个CPU密集型,和1个IO密集型
//获取cpu个数
Runtime.getRuntime().availableProcessors();
执行时间不同的任务交给不同规模的线程池处理,或者使用优先队列让执行时间段的任务先执行;
依赖数据库连接池的任务,由于该线程提交sql后需要等待结果,等待时间越长则cpu空闲时间越长,线程数应该设置大点.
建议使用有界队列,有界队列可以增加系统稳定性和预警能力,可以根据需求设置大点(几千).
线程池监控
可以通过线程池提供的参数进行监控
taskCount 线程池需要执行任务的数量
completedTaskCount 线程池中已经完成的线程数 <=taskCount
largestPoolSize 线程池中曾经创建过最大的线程数量,可以判断线程池是否满过
getPoolSize 线程池的线程数
getActiveCount 活动的线程数
可以重写线程池的beforeExecute afterExecute terminated方法进行.