源码分析之ThreadPoolExecutor

线程池在多线程编程的中可谓是个利器,使用线程池会大大提高多线程的效率。原因是使用线程池相对于new Thread有效避免了线程创建和销毁的开销。

Java中一般来说通过Executors来创建所需要的线程池,如:Executors.newFixedThreadPoolExecutors.newScheduledThreadPool等等。而其中一种主要的实现就是ThreadPoolExecutorThreadPoolExecutorJ.U.C包下的,当然也是出自大神Doug Lea之手。

ThreadPoolExecutor 结构

ThreadPoolExecutor类结构
ThreadPoolExecutor类结构

如上图所示,ThreadPoolExecutor类大致结构如上图所示。Executor接口只有一个execute方法。ExecutorSerivce接口在Executor的基础上提供了对任务执行的管理,如:shutdown方法。AbstractExecutorService是针对其中invokeXXXsubmit做了默认的实现。真正的execute将由ThreadPoolExecutor自身实现。

ThreadPoolExecutor还有几个内部类:AbortPolicyDiscardPolicyDiscardOldestPolicyCallerRunsPolicy。这几个类的作用是当线程池承载很多任务之后,超过maximumPoolSize的数量,无法继续接受任务的时候,提供了不同的拒绝策略。

Worker类也是ThreadPoolExecutor的内部类,实现Runnable接口,提供了工作线程的实现。后面会详细分析这个类的实现。

ThreadPoolExecutor类提供了4个构造方法。其中最核心的构造方法如下所示:

    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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

参数含义如下:

参数 含义
corePoolSize 核心线程池大小
maximumPoolSize 最大线程池大小
keepAliveTime 超过corePoolSize数量的空闲线程最大存活时间
corePoolSize keepAliveTime 时间单位
workQueue 工作队列
ThreadFactory 线程工厂
RejectedExecutionHandler 拒绝策略,默认拒绝策略:AbortPolicy

ThreadPoolExecutor 类变量

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    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; }
    

AtomicInteger ctl

ctl是一个AtomicInteger修饰的变量,用来保存2个变量:

  • workerCount: 线程池中活动线程的数量
  • runState:线程池的运行状态

workerCount

workerCount占用低29位存线程数,workerCount代表了线程池活动的线程数量,最小数量是0,最大的数量是(1 << COUNT_BITS) - 1 (536870911);

1 << COUNT_BITS :

00000000 00000000 00000000 00000001 --> 00100000 00000000 00000000 00000000

(1 << COUNT_BITS) - 1 :

00100000 00000000 00000000 00000000 --> 00011111 11111111 11111111 11111111

private static int workerCountOf(int c)  { return c & CAPACITY; }

workerCountOf方法是用从ctl中解析出workerCount的值来。由于CAPACITY高3位是000ctl的值与CAPACITY&操作的时候,高3位将被舍弃;由于CAPACITY低29位是全是1&操作会保持原值,这样workerCount的值就从ctl中解析出来了。

runState

runState占用高3位存线程状态,共有5个值:

  • RUNNING(-536870912):接受新任务,并处理队列任务
  • SHUTDOWN(0): 不接受新任务,但会处理队列中的任务
  • STOP(536870912): 不接受新任务,不会处理队列任务,中断正在处理的任务
  • TIDYING(1073741824): 所有任务已结束,workerCount为0,线程过渡到TIDYING状态,将调用terminated()方法
  • TERMINATED(1610612736): terminated()方法已经完成
private static int runStateOf(int c)     { return c & ~CAPACITY; }

runStateOf方法的作用是解析出runState的值。~CAPACITY将反转CAPACITY的值,也就是CAPACITY的高3位全部为1,低29位全部为0。如此ctl & ~CAPACITY 就会送ctl中解析出runState的值。

worker 类

线程池维护的是HashSet<Worker> workers,一个由Worker对象组成的HashSet

    // 继承AQS,实现Runnable接口
    private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        // 处理任务的线程
        final Thread thread;
        // worker 传入的任务
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            // 创建一个任务线程
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

Worker继承与AQS,主要想利用AQS独占所机制,来标示线程是否空闲。后续的shutdown方法在执行时候,判断线程是否空闲,tryLock来实现的。同样可以看到在Worker在初始化的时候将state设置为-1,这也是为了避免worker在执行前被中断。

Worker继承与Runnable,当worker启动时候就会调用run方法。Worker类的run方法调用了runWorker方法;

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        // 将state设置为0,允许中断
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // task为空,则调用getTask(),从workQueue取出新的task
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                // 线程池如果是STOP状态,要中断当前线程
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    // 将task设置为null,循环操作
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

execute 方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
            
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

execute主要分3个步骤:

  • 当活动线程小于corePoolSize的值,将会调用addWorker创建新的任务线程;
  • 如果任务可以成功被加入至workQueue,这是将再次获取ctl的值做双重校验,如果线程池已经shutdown,将从workQueue移除并拒绝该任务。
  • 如果任务队列加入失败,则拒绝任务。

addWorker 方法

  private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 线程池状态大于SHUTDOWN值将不再接受新的任务,创建线程。
            // rs == SHUTDOWN or  firstTask == null or workQueue.isEmpty() 都不再接受任务
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                // 可用线程数不足,分配任务失败
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // CAS操作增加workerCount的值,增加成功跳出循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                // 线程池状态变化则重试
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        // workerCount增加成功进入下面的逻辑
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                         // 将任务线程添加到线程池中
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    // 启动任务线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                // 任务线程启动失败调用addWorkerFailed,将线程从线程池移除,将workerCount -1
                addWorkerFailed(w);
        }
        return workerStarted;
    }

shutdown 方法

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 设置线程池状态为:SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 中断所有任务线程
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

调用线程池shutdown方法,将不再接受新的任务。首先会调用checkShutdownAccess方法检验是否具有线程shutdown的权限,然后将线程池的状态设置为SHUTDOWN,最后中断空闲线程,这里的中断是用Thread.interrupt()实现的,所以不会影响正在执行的线程,正在执行的的任务将会继续执行。

shutdownNow 方法

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 将线程池状态设置为:STOP
            advanceRunState(STOP);
            // 中断所有线程
            interruptWorkers();
            // 获取队列中尚未被执行的任务
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

shutdownNow方法与shutdown方法类似,不同是shutdownNow会将线程池的状态设置STOP,在中断过程中,少了tryLock获取锁的操作,所以不管线程是否空闲都将被中断,中断所有线程,但也不会强行终止正在执行的线程。最后返回阻塞队列中没有被执行的任务list

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容