线程池的介绍

  • 为什么要用线程池?

系统中频繁的创建线程、关闭线程是相当消耗资源的,会降低机器的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

使用线程池可以带来一系列好处:降低资源消耗、提高响应速度、提高线程的可管理性等。

  • 创建线程池

 创建线程池推荐使用ThreadPoolExecutor。阿里巴巴开发规范也强制使用ThreadPoolExecutor来创建线程池。

手动创建线程池的构造函数如下:

public ThreadPoolExecutor(int corePoolSize, //核心线程数量
                              int maximumPoolSize, //最大线程数
                              long keepAliveTime, //超时了多久没人调用就释放
                              TimeUnit unit, //超时时间单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory, //线程工厂 创建线程的一般不用动
                              RejectedExecutionHandler handler //拒绝策略
                             

    )
  • 线程池的内部运行原理

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:(1)直接申请线程执行该任务;(2)缓冲到队列中等待线程执行;(3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

  • 线程池的生命周期
查看代码
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;  //==29
    private static final int CAPACITY = (1 << COUNT_BITS) - 1;
    //yds: 高三位记录线程池生命状态,低29位记录当前工作线程数
    // runState is stored in the high-order bits

    //      二进制 负数 用正数的补码表示
    //    1 = 0000 0000 0000 0000 0000 0000 0000 0001
    //        1111 1111 1111 1111 1111 1111 1111 1110  反码(取反)
    //   -1 = 1111 1111 1111 1111 1111 1111 1111 1111  补码(反码+1)
    //   -1 左移29位 1110 0000 0000 0000 0000 0000 0000 0000

    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

    // 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) { //通过状态和线程数量生成ctl
        return rs | wc;
    }

通过阅读源码可知,线程池中用一个 AtomicInteger ctl 变量来同时维护 线程状态线程数量ctl32位长度,高3位表示线程状态,低29位表示线程数量。两个变量之间互不干扰。用一个变量去存储两个值,可避免在做相关决策时,出现不一致的情况,不必为了维护两者的一致,而占用锁资源。

线程池的状态如下

    /**
     * 源码注释
     * RUNNING:  Accept new tasks and process queued tasks
     * SHUTDOWN: Don't accept new tasks, but process queued tasks
     * STOP:     Don't accept new tasks, don't process queued tasks,
     * and interrupt in-progress tasks
     * TIDYING:  All tasks have terminated, workerCount is zero,
     * the thread transitioning to state TIDYING
     * will run the terminated() hook method
     * TERMINATED: terminated() has completed
     */
状态 解释
RUNNING 接受新任务并处理排队任务
SHUTDOWN 不接受新任务,但处理排队任务 
STOP 不接受新任务,也不执行队列中的任务,会中断正在处理中的任务
TIDYING 所有任务已终止, workerCount(工作线程数)为零,转换到状态 TIDYING 的线程将运行 terminate()方法
TERMINATED terminate() 方法已完成后进入该状态

 

线程池的状态之间的转换关系如下

  •  任务调度机制

首先,所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
查看代码
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //1.判断核心线程数是否已满,没满直接添加核心线程来执行任务 (当前线程数量 < 核心线程数 则 添加核心线程)
            if (addWorker(command, true))
                return;
            c = ctl.get();//添加核心线程失败,重新获取ctl
        }
        if (isRunning(c) && workQueue.offer(command)) {//2.线程池运行状态 且 核心线程数量已到达最大 则 将任务添加到阻塞队列(队列未满则添加成功)
            int recheck = ctl.get();//再次获取ctl,
            if (!isRunning(recheck) && remove(command))//3.线程池不在运行状态,则执行拒绝策略{注意,如果线程池不在运行状态,就会删掉(remove)任务,然后在执行拒绝策略}
                reject(command);
            else if (workerCountOf(recheck) == 0)//4.线程池运行正常,工作线程数量==0,{注意:此时已经讲一个任务放入了队列,但是没有工作线程,要去添加非核心线程}
                addWorker(null, false); //添加任务为空的工作线程,处理阻塞队列中的任务,避免队列中没人执行
        } else if (!addWorker(command, false))// 线程池运行状态在运行状态,任务添加队列失败(队列满了),则 添加非核心线程来执行任务,若添加失败则执行拒绝策略
            reject(command);
    }

调度流程图如下:

  • 阻塞队列

常用的阻塞队列有:

  • 拒绝策略

拒绝策略有以下四种:

详细信息如下:

//抛出RejectedExecutionException的被拒绝任务的处理程序。
//new ThreadPoolExecutor.AbortPolicy()

//被拒绝任务的处理程序,直接在execute方法的调用线程中运行被拒绝的任务(main线程执行),除非执行程序已关闭,在这种情况下,任务将被丢弃。
//new ThreadPoolExecutor.CallerRunsPolicy()

//拒绝任务的处理程序,丢弃最旧的未处理请求,然后重试execute ,除非执行程序被关闭,在这种情况下任务被丢弃。
//new ThreadPoolExecutor.DiscardOldestPolicy()

//被拒绝任务的处理程序,它默默地丢弃被拒绝的任务。
//new ThreadPoolExecutor.DiscardOldestPolicy()
  • 线程池执行任务过程

线程池执行任务调用方法如下:可对源码做一个简单分析

接着可以看看addWorker(Runnable firstTask, boolean core) 的源码逻辑

查看代码
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:  //标记外侧循环
        //以下两个循环的目的就是进过判断给工作线程标识+1
        for (; ; ) {
            int c = ctl.get(); //ctl 记录线程池状态和线程池工作状态的
            int rs = runStateOf(c);//线程池状态

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&  // 除了RUNNING的状态
                    !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())  // (a && b && c):abc  有一个false就直接返回false
                //  1.(rs == SHUTDOWN --> 如果rs不是SHUTDOWN则说明是更高的状态,STOP之类的,这时就不需要创建工作线程了)
                //  2.(firstTask == null --> 创建线程处理队列中的任务)  如果任务为null,且线程池状态不是RUNNING 则不需要处理
                //  3. !workQueue.isEmpty()  对列为空,加两次非操作,-->  不需要处理
                    //注意 SHUTDOWN 状态下的线程池是要处理队列中的任务的
            ) {
                //创建工作线程失败
                return false;
            }
            for (; ; ) {
                int wc = workerCountOf(c); //工作线程 个数
                if (wc >= CAPACITY ||  //工作线程大于等于 最大容量 ,无需 创建新的线程
                        wc >= (core ? corePoolSize : maximumPoolSize))  // 工作线程大于等于 (核心或者非核心)线程数
                    return false;

                //将工作线程+1,采取 CAS 的方式
                if (compareAndIncrementWorkerCount(c))
                    break retry;//成功 返回外层循环
                // 失败(线程+1的时候 并发产生资源争抢、锁) 重新获取 ctl
                c = ctl.get();  // Re-read ctl
                //判断线程池状态与开始是否相同 若有变化结束本次外侧循环,进入下次外侧循环;若没有变化重新执行内侧循环
                if (runStateOf(c) != rs)
                    continue retry;  //结束本次外侧循环,进入下次外侧循环
                // else CAS failed due to workerCount change; retry inner loop

            }
        }
        // 到此 完成了 工作线程标识+1 的操作

        boolean workerStarted = false; //worker 开始  false
        boolean workerAdded = false; //worker 添加 false
        Worker w = null;  //工作线程
        try {
            //创建任务 传入任务
            w = new Worker(firstTask);//Worker 实现了 Runnable
            final Thread t = w.thread;  //从Worker 中获取线程
            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 ||   //-->RUNNING
                            (rs == SHUTDOWN && firstTask == null)) {  //--->创建线程测试阻塞队列任务
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();  //给线程已经启动了,抛异常
                        workers.add(w);  //将工作线程添加到集合中  HashSet<Worker> workers
                        int s = workers.size();
                        if (s > largestPoolSize)//largestPoolSize  一直追踪 当前的最大线程数
                            largestPoolSize = s;
                        workerAdded = true;  //添加工作线程成功
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();  //启动工作线程
                    workerStarted = true; //启动工作成功
                }
            }
        } finally {
            if (!workerStarted)  //启动工作失败
                addWorkerFailed(w);
        }
        return workerStarted;  //返回线程是否启动成功
    }

再看Worker源码

查看代码
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); //将worker 自己传进去。thread(Runable runable)调用start()方法的时候  --->实则调用runable.run(),
            // 在Worker 对应下面的run() 方法
        }

        /**
         * Delegates main run loop to outer runWorker
         */
        //重新的run()方法
        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) {
                }
            }
        }
    }

 再看runWork()方法源码:

查看代码
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();//获取当前线程
        Runnable task = w.firstTask; //拿到任务
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //  情况1:任务不为空--->执行任务
            //  情况2:任务为空---> 通过 getTask() 从阻塞队列中获取任务
            while (task != null || (task = getTask()) != null){ // 此处 用 "与" 操作,task==null(核心线程和非线程都无任务执行)的情况下才会getTask(队列获取任务)。线程池的执行优先级阻塞队列里面的最后执行
                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
                if ((runStateAtLeast(ctl.get(), STOP) ||  //判断线程池状态是否>= STOP  即线程池 is stoping
                     (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;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);//获取不到任务时,主动回收自己
        }
    }

再看getTask()源码

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())) {  // 线程池状态至少为 SHUTDOWN  -->{1:此案城池至少为STOP  || 2:队列为空}
                decrementWorkerCount();  //工作线程-1
                return null;
            }

            int wc = workerCountOf(c);  //工作线程个数重新获取一下。 次数说明线程池人在 RUNNING 状态

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //  允许核心线程超时 || 工作线程>核心线程数

            if ((wc > maximumPoolSize || (timed && timedOut))  //工作线程数 > 最大线程数  或 [ timed == true : (允许核心线程超时 || 工作线程>核心线程数)  && timedOut  == true: 上次在队列里面拉取线程超时 ]
                    && (wc > 1 || workQueue.isEmpty())) {  // 工作线程 > 1 或 队列为空
                if (compareAndDecrementWorkerCount(c)) //利用CAS 对工作线程 -1  成功 返回 null;失败 结束本轮循环
                    return null;
                continue;
            }

            try {
                //time  = true:poll(,,)超时等待拉取任务,keepAliveTime没有获取到有效任务,则返回null
                //time  = false:take()阻塞拉取任务,只到获取有效任务。
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :  //poll 超时 return null
                        workQueue.take();  // 一般不会为空
                if (r != null)
                    return r; //  r!=null :返回队列中获取的任务
                timedOut = true; // r==null : 超时,进如下一轮循环
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

再看看processWorkerExit()源码

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        // completedAbruptly == true : runWorker() 没有正常执行完成,出现了异常。线程数量-1
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks; // 任务完成计数器
            workers.remove(w); //从工做线程集合中移除当前 工作线程
        } finally {
            mainLock.unlock();
        }

        // 根据线程池状态进行判断是否结束线程池
        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {  //线程池状态 RUNNING,SHUTDOWN 的时候进入本方法说明了异常中断了,--> 需要判断是否-- addWorker(null, false);
            if (!completedAbruptly) {  //runWorker(Worker w)  执行正常
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;  // min : 至少需要的工作线程数
                if (min == 0 && !workQueue.isEmpty())  // 假如队列有任务等待  则至少保留一个工作线程
                    min = 1;
                if (workerCountOf(c) >= min) // 当前工作线程数满足最少需要的线程数  则不用addWor();
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

 

posted @ 2022-03-18 16:47  iyandongsheng  阅读(93)  评论(0)    收藏  举报