自从上篇博客过去一个多月了,真不是忘了写,而是最近写了一个项目,一个月呕心沥血终于把负责的地方写完了。
今天来回顾下项目中用到的线程池。
线程池的核心参数:
int corePoolSize:该线程池中核心线程数最大值
核心线程:线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程。
核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干、
int maximumPoolSize:该线程池中线程总数最大值
线程总数=核心线程数+非核心线程数。
long keepAliveTime:该线程池中非核心线程闲置超时时长
一个非核心线程,如果不干活的时长超过这个参数所设定的时长,就会被销毁掉。
TimeUnit:时间单位
keepAliveTime的单位,timeUnit是一个枚举类型。
BlockingQueue<Runable> workQueue:定义阻塞队列
该线程池中的任务队列;维护着等待执行的Runnable对象。当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了则新建非核心线程执行任务。
ThreadFactory:线程工厂一般使用默认
创建一个新线程使用的工厂,可以用来设定线程名,是否为daemon线程等等。
RejectedExecutionHandler:拒绝策略
AbortPolicy:直接抛出RejectedExecutionHandler异常阻止系统正常运行这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态,如果是比较关键的业务,推荐使用此拒绝策略,这样子系统不能承载更大的并发量的时候,能够及时通过异常发现。
CallerRunsPolicy:调用者运行,一种调用机制,该策略即不会抛弃任务,也不会抛异常,而是将某些任务退回到调用者,从而降低新任务流量。此拒绝策略由调用线程直接执行被丢弃任务。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中尝试再次提交当前任务,此拒绝策略,是一种喜新厌旧的拒绝策略,是否要采用此拒绝策略还得根据实际业务是否允许丢弃老任务来认真衡量。
DiscardPolicy:该策略默默丢弃无法处理的任务,不予任何处理 也不抛弃出异常,若果允许任务丢弃,这是最好的一种策略,使用此策略可能会使我们无法发现系统中的异常状态,建议是一些无关紧要的业务采用此策略。例如,博客网站统计阅读量就是才用这种拒绝策略。
线程池的执行流程
在创建线程池后,线程池中线程数为零(惰性加载)。
当调用execute()方法添加一个请求任务时,线程池会做出如下判断:
如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
如果正在运行的线程数量大于corePoolSize,那么将这个任务放入阻塞队列;
如果这个时候阻塞队列已满,且正在运行的线程数小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略。当一个线程完成任务时,他会从队列中取下一个任务来执行。
当一个线程无事可做超过一定时间(keepAliveTime)时,线程会判断:如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。