那么线程池是如何把线程统一分配调度与监控的呢?
【画图太累了,我就用的网图,图片来源视频:https://www.bilibili.com/video/av88030891 】
【画图太累了,我就用的网图,图片来源:https://blog.csdn.net/lchq1995/article/details/85230399 】
我们在NEW一个最基本的线程池的时候,会传入这么一下几个参数:
corePoolSize:线程池核心线程数量
maximumPoolSize:线程池最大线程数两
keepAliveTime:空闲线程存活时间
- corePoolSize顾名思义就是最大核心线程数量,是线程池可以同时执行的线程数量。
- maximumPoolSize,既然有corePoolSize,那么如果corePoolSize满了怎么办呢?这时候就会用到一个队列,叫阻塞队列(Block Queue)
什么是BlockQueue?它有什么特点?
既然是Queue,那么久满足队列模型的(FIFO)原则,一端放入,另一端取出。(First In First Out)
阻塞队列有一个特点就是:在任意时刻,不管并发量有多高,永远只有一个线程能进行队列的如对或出队,所以BlockQueue是一个线程安全的队列。
并且如果队列满了,只能进行出队操作,所有入队操作必须等待,也就是阻塞。
如果队列为空,那么就只能进行入队操作,所有出队操作必须等待,也就是阻塞。
一旦线程池的线程量满了,那么新被execute进来的线程,就会被存储进BlockQueue,BlockQueue的大小就是maximumPoolSize - corePoolSize的大小。
线程池和五种状态:
- Running:能接受新的execute以及处理已添加的任务
- Shutdown:不接收任何新的execute,可以处理已添加的任务
- Stop:不接受任何新的execute,不处理已添加的任务
- Tidying:所有任务已经终止,ctl记录的任务数量为0.
- Termiated:线程池彻底终止,则线程池转换为Terminated状态。
那么这么多线程池状态和这么多线程的信息,是如何保存的呢?
这里线程池内部用到了一个32字节的Integer类型来记录线程池的状态和线程数量信息。
这个Integer类型的高3未二进制用来表示线程池的状态,后29为用来表示线程的数量。
线程池定义了这么几个数字作为线程的状态
RUNNING = -1
SHUTDOWN = 0
STOP = 1
TIDYING = 2
TERMINATED = 3
并且所有数字都想做移位29位。
《《 COUNT_BITS(COUNT_BITS=29)
最终会得到高三位为:
RUNNING = 111
SHUTDOWN = 000
STOP = 001
TIDYING = 010
TERMINATED = 011
他是怎么得到的呢?
我们来回顾一下基础
拿-1来举个例子
众所周知,
1在32位Integer的类型中二进制为:
0000 0000 0000 0000 0000 0000 0000 0001
那么-1就应该去1的反位并且再加上一个符号位1000
则,-1就应该为:
1000 1111 1111 1111 1111 1111 1111 1111
那么-1向左移位29位,低位补0,那么则
-1 《《 29 等于
1110 0000 0000 0000 0000 0000 0000 0000
所以高三位为111
所以这就是RUNNING的高三为为111的由来
后面的29位用于存储线程的数量。
这种应用基本数据高效存储的思想可以用于存储一些记录,有点就是不用去多个变量的读取,提升速度。
具体线程池的实现可以百度搜索JAVA线程池的实现,在这里只是浅谈一下线程池的好处以及浅层原理。
(本文完)