一、 ThreadPoolExecutor中的线程池运行状态
ThreadPoolExecutor中控制线程池状态是由一个AtomicInteger
对象ctl
来表示的,取前3位来表示状态。
//ThreadPoolExecutor中的部分代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//表示线程池状态和线程数量
private static final int COUNT_BITS = Integer.SIZE - 3; //aka 29
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//aka (2^29)-1
private static final int RUNNING = -1 << COUNT_BITS; //最高三位为111
private static final int SHUTDOWN = 0 << COUNT_BITS; //...... 000
private static final int STOP = 1 << COUNT_BITS; // 001
private static final int TIDYING = 2 << COUNT_BITS; // 010
private static final int TERMINATED = 3 << COUNT_BITS; // 011
也就是说,ThreadPoolExecutor的线程池最大容量即(2^29-1)。
private static int runStateOf(int c) {
return c & ~CAPACITY; }
private static int workerCountOf(int c) {
return c & CAPACITY; }
private static int ctlOf(int rs, int wc) {
return rs | wc; }
以上三个方法是使用了位运算来分别获得:线程池运行状态、线程数量以及ctl
的值。
与运算的特点就是同为1的情况下结果才为1,其余为0;或运算则是有一个为1的情况下就为1,当同为0的时候结果才是0。
第一个方法runStateOf
是将c
跟默认的CAPACITY
取反后再做与运算,CAPACITY
是低29位都为1的一个数,取反之后就只有前三位为1了,再结合第一段分析的状态表示,即可知道这个方法是用来获取线程池运行状态。
第二个方法workerCountOf
是将c
跟默认的CAPACITY
做与运算,那么就可以获得低29位的情况,也就是默认情况下线程池中的线程数量。
第三个方法ctlOf
就是将workerCount
跟runningState
合并成ctl
。
而线程池的状态具体意义如下:
RUNNING:处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。
SHUTDOWN:处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。
STOP:处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
TIDYING:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
TERMINATED:线程池彻底终止的状态。
以上就是线程池状态的简要分析了,在java源码中能看到很多地方使用位运算来压缩状态的体积,比如说在读写锁ReentrantReadWriteLock
中也是用了一个整型变量来表示读锁和写锁的状态,其中高16位表示读状态而低16位则表示写状态。
二、 ThreadPoolExecutor中的构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//...
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
//acc为AccessControlContext对象,具体只在ThreadPoolExecutor的finalize方法中用到。
//而这一个类我根据注释翻译了一下,是用于根据压缩的context来决定使用哪一个系统资源;具体的还希望有大佬来解释解释。
//...
}
ThreadPoolExecutor
最终执行的构造器如上所示,一共有7个参数。
参数名 | 参数类型 | 参数具体作用 |
---|---|---|
corePoolSize | int | 线程池中核心池的大小,也就是说核心池中的线程最大数量。核心池中的线程即使没有任何的任务去工作,这些线程也不会被销毁,除非设置了allowCoreThreadTimeOut |
maximumPoolSize | int | 线程池的线程最大数量 |
keepAliveTime | long | 线程池中非核心池线程或设置了allowCoreThreadTimeOu t时,线程根据这个值检查活性,一旦超时则销毁线程,也就是说如果设置了allowCoreThreadTimeOut ,那么就没有核心线程这一概念了 |
unit | TimeUnit | 线程keepAliveTime的时间单位 |
workQueue | BlockingQueue | 线程的等待队列 |
threadFactory | ThreadFactory | 用于构造线程 |
handler | RejectedExecutionHandler | 拒绝执行线程的处理器 |
使用线程池的时候,最好不要使用默认的实现(指的是直接使用Executors
里面的一些静态方法生成的线程池)而是自己new
一个,因为在这些默认的实现中workQueue
使用了new LinkedBlockingQueue<Runnable>()
来创建线程的等待队列,点进去可以看到他的最大容量是Integer.MAX_VALUE
。一旦线程等待的数量非常大,就会产生OutOfMemoryException
。因此最好是自己主动new一个线程池。
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
为了验证这一个问题,使用-Xmx1m
将jvm运行内存设置为 1mb,测试代码如下:
public class OomTest