ThreadPoolExecutor类中有四个构造方法,它们实际执行的都是这个构造方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
// 这三个参数都不能为空
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
(1)int corePoolSize:核心线程数。
(2)int maximumPoolSize:最大线程数,线程池中允许创建的最大线程数。
(3)long keepAliveTime:空闲线程的存活时间。如果某线程的空闲时间超过这个值,那么这个线程就可以关闭了。这个存活时间并不会对所有线程都起作用,如果线程池中的线程数小于等于corePoolSize,那么这些线程不会因为存活时间太长而关闭。当然,也可以调用allowCoreThreadTimeOut(true)使核心线程数内的线程也可以被回收。
(4)TimeUnit unit:存活时间的单位。
(5)BlockingQueue<Runnable> workQueue:任务队列,BlockingQueue接口的某个实现类对象(常使用ArrayBlockingQueue和LinkedBlockingQueue)。
(6)ThreadFactory threadFactory:用于生成线程,一般使用默认的就可以。可以通过这个参数使线程名称更具可读性,例如Message-Thread-1,Message-Thread-2。
(7)RejectedExecutionHandler handler:当线程池已满,又有新任务提交时,采取什么样的策略。有几种策略可供选择,例如抛出异常、直接拒绝并返回等,也可以自己实现相应的接口以实现自己的逻辑。
然后我们再看看其它的重要属性。
有一个32位的整数用于存放线程池的状态和线程数,其中高3位用于存放线程池状态,低29位用于存放线程数
Doug Lea 采用一个 32 位的整数来存放线程池的状态和当前池中的线程数,其中高 3 位用于存放线程池状态,低 29 位表示线程数(即使只有 29 位,也已经不小了,大概 5 亿多,现在还没有哪个机器能起这么多线程的吧)。我们知道,java 语言在整数编码上是统一的,都是采用补码的形式,下面是简单的移位操作和布尔操作,都是挺简单的。
// ctl的高3位代表线程池的状态,低29位代表线程池的线程数。
// ctl初始值为11100000000000000000000000000000
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 32-3=29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池最大容量为2^29-1=536870911,00011111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 11111111111111111111111111111111左移29位,得到11100000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
// 00000000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
// 01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
// 01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
// 将c的低29位设置为0。可以据此获得线程池的状态。
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 将c的高3位设置为0。可以据此获得线程池的线程数。
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
线程池的各种状态
(1)RUNNING(-1):正常状态。也是线程池的初始状态。
(2)SHUTDOWN(0):优雅关闭状态。不接受新的任务,但会继续处理等待队列中的任务(已添加的任务)。
(3)STOP(1):不接受新的任务,不再处理等待队列中的任务(已添加的任务),尝试中断正在执行任务的线程。
(4)TIDYING(2):所有已终止,workCount为0。线程池在转换到这个状态时,会执行terminate()方法。
(5)TERMINATED(3):线程池彻底停止。terminated()方法执行完毕后线程池的状态。
线程池的状态转换过程
(1)RUNNING -> SHUTDOWN:调用shutdown()方法后,会发生这个状态转换。
(2)(RUNNING/SHUTDOWN) -> STOP:调用shutdownNow()方法后,会发生这个状态转换。
(3)SHUTDOWN -> TIDYING:当线程池和任务队列的任务执行完毕后,会发生这个状态转换。
(4)STOP -> TIDYING:当线程池中执行的任务为空,会发生这个状态转换。
(5)TIDYING -> TERMINATED:terminated()方法执行完毕后,会发生这个状态转换。
网友评论