什么是线程池?线程池ThreadPoolExecutor使用及其原理又是什么?

线程池,顾名思义,用来存放线程的一个容器
先了解一下线程的生命周期
什么是线程池?线程池ThreadPoolExecutor使用及其原理又是什么?

文章插图
 
我们为什么要用线程池?技术的发展无非就是需求推动的,而技术领域的需求大部分都是快!再快!更快!
那么线程池出现的需求也就是痛点是什么呢?
第一、线程的创建和销毁是要占用一定的资源的,创建线程会直接向系统申请,调用系统函数进行分配资源 。操作系统给线程分配内存、列入调度,同时线程还要进行上下文的切换 。
第二、在JAVA中,线程的线程栈所占用的内存在Java堆外,不受Java程序控制,只受系统资源限制,默认一个线程的线程栈大小是1M(当然这个可以通过设置-Xss属性设置,但是要注意栈溢出问题) 。如果每个请求都新建线程,1024个线程就会占用1个G内存,系统很容易崩溃 。
第三、我们常用的多线程技术主要解决处理器单元内多个线程执行的问题,它的作用显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力 。假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间 。在这种情况下经常会遇到T1+T3远远大于T2的情况,多线程反而成了负担 。
为了解决这些痛点,出现了线程池这个概念 。
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务加入队列,然后在线程创建后启动这些任务,如果先生超过了最大数量,超出的数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行 。
他的主要特点为:线程复用、控制最大并发数、管理线程 。
第一:降低资源消耗,通过重复利用自己创建的线程降低线程创建和销毁造成的消耗 。
第二: 提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行 。
第三: 提高线程的可管理性 。线程是稀缺资源,如果无限的创建,不仅会消耗资源,还会较低系统的稳定性,使用线程池可以进行统一分配,调优和监控 。
java自带的线程池工厂java自带有一个线程池工厂,工厂里面的线程池分了如下几类:
Executors.newSingleThreadExecutor:创建一个单线程的线程池 。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务 。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它 。此线程池保证所有任务的执行顺序按照任务的提交顺序执行 。
Executors.newFixedThreadPool:创建固定大小的线程池 。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小 。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程 。
Executors.newCachedThreadPool:创建一个可缓存的线程池 。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务 。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小 。
Executors.newScheduledThreadPool:创建一个大小无限的线程池 。此线程池支持定时以及周期性执行任务的需求
Executors.newWorkStealingPool:这个是jdk1.8新增的线程池,适合处理比较耗时的工作任务 。
实际使用的线程池既然java1.8自带了这么多线程池,我们平时生产中用那个呢?
抱歉,都不用 。
为啥呢?
先看一眼线程池工作流程:
什么是线程池?线程池ThreadPoolExecutor使用及其原理又是什么?

文章插图
 
如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;如果这时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行 。
然后咱再看看阿里规约:
什么是线程池?线程池ThreadPoolExecutor使用及其原理又是什么?

文章插图
【什么是线程池?线程池ThreadPoolExecutor使用及其原理又是什么?】 
喏,阿里规约写得很明白,这四个线程池有两个不限制队列长度,有两个不限制线程数,这在极高并发下是非常危险的,比如阿里的双十二,绝对秒炸 。而且,没有合适的拒绝策略,虽然这四个要么不限制队列长,要么不限制线程数的线程池看起来都用不到,所以拒绝策略就是抛个异常就没了 。


推荐阅读