logo头像
从未如此简单有趣

解读Java线程池

前言

总结学习下线程池的内部逻辑

继承关系

graph BT
    a(ExecutorService)-->b(Executor)
    c(AbstractExecutorService)-->a
    d(ThreadPoolExecutor)-->c

ThreadPoolExcutor

  1. corePoolSize

    核心线程数

  2. maximumPoolSize

    最大线程数,线程池允许创建的最大线程数

  3. workQueue

    任务队列,BlockingQueue接口的某个实现

  4. keepAliveTime

    空闲线程的保活时间,如果某线程的空闲时间超过这个,那么可以被关闭了。注意这个值并不好对所有的线程起作用,如果线程池中的线程数少于等于核心线程数corePoolSize,那么这些线程不会因为空闲太长时间而被关闭,当然也可以通过调用allowCoreThreadZTimeOut(true)使核心线程数内的线程也可以被回收。

  5. threadFactory

    用于生成线程的,一般用来给该线程池中的线程取个容易区分的name。

  6. handler

    当池中线程达到最大线程数,同时等待队列也已满了。这时再来任务就会被拒绝,并调用handler的rejectedExecution()方法
    Doug Lea采用一个32位的整数来存放线程池的状态和当前池中线程数,高3位用于存状态,低29位表示线程数(最多大概5亿多个线程)

     // 将整数c的低29位修改为0,就得到了线程池的状态
     private static int runStateOf(int c)     { return c & ~CAPACITY; }
     // 将整数c的高三位修改为0,就得到了线程池中线程数
     private static int workerCountOf(int c)  { return c & CAPACITY; }
     private static int ctlOf(int rs, int wc) { return rs | wc; }
    

线程池的状态

  • Running:接收新的任务,处理等待队列中的任务
  • Shutdown:不接受新的任务提交,但是会继续处理等待队列中的任务
  • Stop:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程
  • Tidying:所有任务都销毁了,workCount为0.线程池的状态转换为Tidying时,会执行钩子方法terminated()
  • Terminated:terminated()方法结束后,线程池就是这个状态了。

    Running定义为-1,shutdown定义为0,其他的都比0大,所以等于0的时候不能提交任务,大于0的话,连正在执行的任务也需要中断。

状态转换

  • Running -> Shutdown:当调用了shutdown()后,会发生这种状态转换。
  • (Running or Shutdown) —> Stop:当调用ShutdownNow()后,会发生这种转换。
  • Shutdown->Tidying:当任务队列和线程池都清空后,会由Shutdown转换为Tidying
  • Stop->Tidying:当任务队列清空后,发生这个转换。
  • Tidying->Terminated:当terminated()方法结束后

Worker

继承于AbstractQueuedSynchronizer,对就是继承于大名鼎鼎的AQS,因为涉及到了多线程操作,就存在同步互斥问题,AQS是现成的用于处理同步互斥问题的。

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;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        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) {
                }
            }
        }
    }

execute方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        // 如果当前线程数小于核心线程数,那么直接添加一个worker来执行任务。创建一个新的线程,并把当前任务command作为这个线程的第一个任务(firstTask)
        if (workerCountOf(c) < corePoolSize) {
            // 如果添加成功那么久结束,返回false代表线程池不允许提交任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 到这说明要么当前线程数打印等于核心线程数,要么刚刚addWorker失败。如果线程池处于Running状态,把这个任务添加到workQueue中
        if (isRunning(c) && workQueue.offer(command)) {
            /* 这里说明任务进入了workQueue,我们是否需要开启新的线程
            线程数在[0,corePoolSize)是无条件开启新的线程
            如果线程数已经大于等于corePoolSize,那么将任务添加到队列中然后进行到这里
            */
            int recheck = ctl.get();
            // 如果池不再运行,则移除任务并且执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果池还是Running的,并且线程数==0,开启新线程。
            // case:提交任务到队列了,但是线程都关闭了。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 若workQueue队列满了,那么以maximumPoolSize为界创建新的worker,如果失败说明线程数已经达到maximumPoolSize,执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker(Runnable firstTask, boolean core)

第一个参数是准备提交给这个线程执行的任务,可以为null,第二个参数true表示使用核心线程数corePoolSize作为创建线程的界限,也就说创建这个线程的时候若线程池中的线程总数已经达到corePoolSize那么不能响应本次创建,如果为false表示以最大线程数maximumPoolSize作为界限。

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            // 若线程池已关闭,并满足下条件之一,那么不创建新的worker:1. 线程池状态大于Shutdown,也就是Stop、Tidying、Terminated。2.firstTask != null。3.workQueue.isEmpty()
            // 简单分析如下:还是状态控制的问题,当线程池处于Shutdown时,不允许提交任务,但已有的任务继续执行,当状态大于Shutdown时,不允许提交任务且中断正在执行的任务。
            // case: 当线程池处于Shutdown,但是firstTask为null且workQueue非空,那么是允许创建worker的。这是因为Shutdown的语言是:不需要提交新的任务,但是要把已经进入到workQueue的任务执行完。
            // Check if queue empty only if necessary.
            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;
                // 如果成功,那么就是所以创建线程的条件校验都满足了,准备创建线程执行任务了。如果失败的话,说明有其他线程叶子尝试往线程池中创建线程
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                // 由于有并发,重新再读取一些ctl
                c = ctl.get();  // Re-read ctl
                // 正常如果是CAS失败的话,进到下一个里层的for循环就可以了。如果是因为其他线程的操作导致线程池状态变更,如有其他线程关闭线程池,那么需要回到外层for循环
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        // 到这里可以开始创建线程来执行任务了。
        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());
                    // 小于Shutdown就是Running状态,等于Shutdown不再接收新任务,但是会继续执行等待队列中的任务。
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        // worker中的线程可不能提前开启了
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 加到全局workers(HashSet)中
                        workers.add(w);
                        int s = workers.size();
                        // largestPoolSize用于记录workers中的个数最大值。
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 添加成功了,才能启动线程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            // 没添加成功就做一些清理工作。
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    // 失败就删除对应worker,workcount减1
    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

worker中线程start后就值run方法调用runWorker方法。

    public void run() {
        runWorker(this);
    }
    // worker中线程启动后,while循环不断从等待队列中获取任务并执行。
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // 循环调用getTask()
            while (task != null || (task = getTask()) != null) {
                单个worker的锁
                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 {
                    // 子类可以实现,用于在task.run()之前调用
                    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,准备getTask下一任务
                    task = null;
                    // 累计完成数+1
                    w.completedTasks++;
                    // 释放当前worker独占锁
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            // 1.执行到这说明getTask返回null,也就是队列中已经没有任务需要执行了,执行关闭。
            // 2.任务执行过程中出现异常
            // 第一种已经在代码处理了workCount减一,这个在getTask中。
            // 第二种情况,workCount没有进行处理,所以需要在processWorkerExit中处理。
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask()获取任务

此方法有三种可能:

  1. 阻塞直到获取任务返回,默认corePoolSize之内的线程不会被回收,它们会一直等待任务。
  2. 超时退出。keepAliveTime起作用的时候,也就是如果这么多时间内都没有任务,那么应该执行关闭。
  3. 如果发生以下条件,此方法必须返回null,用于关闭该线程。
    • 池中有大于maximumPoolSize个workers存在(通过调用setMaximumPoolSize进行设置)
    • 线程池处于Shutdown,而且workQueue是空的,这种不接受新的任务
    • 线程池处于stop,不仅不接受新的线程,连workQueue中的线程也不再执行。
private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                // cas操作减少工作线程
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);
            // 允许核心线程数内的线程回收或线程数超过了核心线程数,就会超时关闭
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            // 线程数大于maximumPoolSize或者超时并且线程大于1或工作队列为空,都可能返回null
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                // cas失败,就继续for循环
                continue;
            }
            // wc <= maximumPoolSize同时没有超时
            try {
                // 取任务
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                // 如果worker发生了中断,采取的方案是重试
                // 为什么会发生中断:可以看下setMaximumPoolSize方法
                // 如果开发者将maximumPoolSize调小了,导致其小于当前workers数量,那么意味着超出的部分线程要被关闭,重新进入for循环自然有部分返回null
                timedOut = false;
            }
        }
    }

拒绝策略

final void reject(Runnable command) {
    handler.rejectedExecution(command, this);
}

这里的handler就是构造线程池时传入的参数,继承于RejectedExecutionHandler。在ThreadPoolExecutor中有四个已经定义好的实现类供我们直接使用,我们也可以自己实现。


    /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
    // 只要线程池没有关闭,那就交由提交任务的线程自己执行这个任务。
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

    /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
    // 直接抛出RejectedExecutionException异常,这是默认的策略
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

    /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    // 不做处理,直接忽略
    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    // 若线程池没有关闭,那么把队列对头的任务直接扔掉,然后提交这个任务到等待队列中
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }