线程池 Thread_Pool
一. 使用线程池的好处 :
1. 避免的线程的创建与销毁,降低了资源的损耗.
2. 由于1的效果,所以响应时间也会相应的缩短,提高响应速度.
3. 可以对系统内的线程进行较为合理的管理,线程总数的控制,以便后续的优化
二. 接下来源码
package java.util.concurrent;
1. 用户可以设定核心线程数,最大线程数=核心线程数,阻塞队列 LinkedBlokingQueue
a. 明显看到核心为ThreadPoolExecutor对象.
b . 因为核心线程数=最大线程数,不存在临时线程.这种线程池,阻塞队列默认是无限大的,见下截图大小为 Integer.Max_Value
2. 核心线程数和最大线程数=1,也是一个默认无限大的队列
a . 同样核心对象为 ThreadPoolExecutor.
3. 不存在核心线程, 但是临时线程默认为无限大.并且这些临时线程在非工作状态下,存活时间为60s.
a. 插入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。同样,如果线程尝试获取元素并且当前不存在任何元素,则该线程将被阻塞,直到线程将元素插入队列。在SynchronousQueue内部没有任何存放元素的能力。
4. 可以设定核心线程.此线程池还带有延迟和周期性执行任务的功能
三 . 创建一个线程池,核心参数会有哪些
1. coreThreadPool //核心线程数
2. maxThreadPool //最大线程数
3. keepAliveTime //非核心线程在非工作的状态时,可以存活时间值
4. unit // 值的单位
5. BlokingQueue // 阻塞队列
6. ThreadFactory // 线程的创建工厂
7. RejectExcitionHandler //拒绝策略
四 . 具体来看一下 newFixedThreadPool 这个类型的线程池
接下来具体代码,与上面流程图是一致的.
a . 做了一些基础的校验,其他的什么也没做,可以比较明了的看到线程池刚创建的时候,并没有初始化任何线程的.
b . 当开始执行execute()方法
a . 当传入命令为空的时候,报出空指针.
a . ctl 这个对象比较关键,在线程池中,ctl贯穿在线程池的整个生命周期中其记录了目前已启用的核心线程数和目前线程池的状态
1. Integer.SIZE = 32 ; Integer 占 4 Byte , 1Byte = 8bit ,所以总共占32位. int count_bits = 32 -3 = 29
2. CAPACITY = (1 << COUNT_BITS) - 1; 1向左移动29位,再 -1 , 标识最大的线程容量(后29位存储线程数)
3. 各个状态都是向左移动count_bits(29)位, 高位的3位来标识线程池状态 . 之所以这么设计,别问,问就是位移计算效率高.
b . 从上面的枚举也可以看到线程池的枚举有哪些
RUNNING SHUTDOWN STOP TIDYING TERMINATED
a . workerCountOf(c) < corePoolSize , 如果当前线程池内的工作线程数小于核心线程数
a . addWorker(command, true) , 就添加一个worker, 参数1 command(新线程要执行的命令); 线程2 true (表示这个线程为核心线程).
a . 这里引入了一个retry关键字. 当双重for循环嵌套的时候,如何能直接跳到目标位置. break retry 和 continue retry 就是除了其本身的作用外,还会跳到retry处.
b . rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()) , 这个是判断如果rs(线程池状态>=0),也就是非运行状态等等,会报出新增线程失败.
a . 这里可以看到Worker是一个内部类,implements RunAble. 代表这也需要重写它的run方法,其实线程池就是通过控制Worker的个数,来控制线程池内线程的个数 ,run()方法内部就是调用了目标类的方法.
b. 如果核心线程数已经够了,那么就需要往队列中插入了.这里采用了 BlokingQueue.offer(command)方法来插入队列.
如果插入成功,但是线程池如果变为非运行状态,会remove(command),并采用拒绝策略.
c . 如果插入队列失败,接下来就开始采用临时线程 ,addWorker(command, false) 可以看到这里的标志位为false.如果加入失败,同样采用拒绝策略.
有哪些拒绝策略:
a . 最后一个为 Tomcat提供的,可以先不管.也就是concurrent包内提供了4种淘汰策略.四个实现都是ThreadPoolExecutor的内部类.
1. AbortPolicy
a . 直接throws异常.
2. DiscardPolicy
a . 什么也不做,这个任务线程池不会再去管了.
3. DiscardOldPolicy
a . Queue.poll()方法(获取队列中的头元素,并删除),与remove()不一样的是,remove()方法当队列中元素为空时, 会抛出java.util.NoSuchElementException异常, poll会返回null.
b . 所以这个策略的含义就是 : 抛弃队列中的最早的任务,并重新开始调用execute()方法.
4. CallRunPolicy
a . 直接在这个execute()线程中,执行目标任务的run()方法.如果当前线程池是非运行状态,那么这个任务就抛弃了