ThreadPoolExecutor

2018-10-09  本文已影响0人  永远的太阳0123

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()方法执行完毕后,会发生这个状态转换。

上一篇下一篇

猜你喜欢

热点阅读