线程池,就是在调用线程的时候初使化一定数量的线程,有线程过来的时候,先检测初使化的线程还有空的没有,
没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去处理;但如果已经达到了最大数,
另外的线程就只有等了,直到有新的线程为止。线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,
保证系统不会因为大量的并发而因为资源不足挂掉。
(就像餐馆中吃饭一样,从里面叫一个服务员出来;就相当于服务员已经用完了,直到有新的“服务员”为止。)
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 500, 5L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(2000),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
- 6:核心线程数
- 500:最大线程数
- 5L:存活时间
- TimeUnit:MILLISECONDS:存活时间单位
- new LinkeBlockingQueue<Runnable>(2000):线程池队列
- Executors.defaultThreadFactory():线程池工厂
- new ThreadPoolExecutor.CallerRunsPolicy():拒绝策略
拒绝策略:
1.AbortPolicy中止策略:丢弃任务并抛出RejectedExecutionException异常。
这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。
如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
功能:当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程.
使用场景:这个就没有特殊的场景了,但是有一点要正确处理抛出的异常。ThreadPoolExecutor中默认的策略就是AbortPolicy,
ExecutorService接口的系列ThreadPoolExecutor因为都没有显示的设置拒绝策略,所以默认的都是这个。
但是请注意,ExecutorService中的线程池实例队列都是*的,也就是说把内存撑爆了都不会触发拒绝策略。
当自己自定义线程池实例时,使用这个策略一定要处理好触发策略时抛的异常,因为他会打断当前的执行流程。
2.DiscardPolicy丢弃策略:ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。例如,本人的博客网站统计阅读量就是采用的这种拒绝策略。
功能:直接静悄悄的丢弃这个任务,不触发任何动作。
使用场景:如果你提交的任务无关紧要,你就可以使用它 。因为它就是个空实现,会悄无声息的吞噬你的的任务。所以这个策略基本上不用了。
3.DiscardOldestPolicy弃老策略:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量。
功能:如果线程池未关闭,就弹出队列头部的元素,然后尝试执行
使用场景:这个策略还是会丢弃任务,丢弃时也是毫无声息,但是特点是丢弃的是老的未执行的任务,而且是待执行优先级较高的任务。
基于这个特性,想到的场景就是,发布消息和修改消息,当消息发布出去后,还未执行,此时更新的消息又来了,
这个时候未执行的消息的版本比现在提交的消息版本要低就可以被丢弃了。因为队列中还有可能存在消息版本更低的消息会排队执行,
所以在真正处理消息的时候一定要做好消息的版本比较。
4.CallerRunsPolicy调用者运行策略:由调用线程处理该任务。
功能:当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理。
使用场景:一般在不允许失败的、对性能要求不高、并发量较小的场景下使用,因为线程池一般情况下不会关闭,也就是提交的任务一定会被运行,
但是由于是调用者线程自己执行的,当多次提交任务时,就会阻塞后续任务执行,性能和效率自然就慢了。