9.线程池的实现,源码中常用的方法

一:程池的作用

      线程池可以使线程复用,避免了每次线程都new一个新的线程,另外我们可以给线程池一个固定大小,从而避免了大量线程对CPU的占用。

二:线程池的创建

        在Java用有一个Executors工具类,可以为我们创建一个线程池,其本质就是new了一个ThreadPoolExecutor对象,

ThreadPoolExecutor参数最全的构造方法:

ThreadPoolExecutor(int corePoolSize,

                                 int maximumPoolSize,

                                 long keepAliveTime,

                                TimeUnit unit,

                                BlockingQueue workQueue,

                                ThreadFactory threadFactory,

                                 RejectedExecutionHandler handler) 

       corePoolSize:线程池的核心线程数,当线程池中的工作线程数小于核心线程数的时候,只要向线程池指派任务,线程池就会创建工作线程。     

       maximumPoolSize:线程池最大工作线程数,当线程池中的工作线程达到最大数的时候,即使再向线程池指派任务,线程池不会创建工作线程。    

       keepAliveTime:当线程池的工作线程数大于核心线程数的时候,多余核心线程数的部分线程可以保持keepAliveTime的空闲时间,当keepAliveTime时间内还没有获取到任务,这些线程后就会被回收。     

       unit:保持空闲时间的时间单位。     

       workQueue:任务队列,当线程池里面核心线程都在工作的时候,再向线程池指派任务,线程池会将任务放入任务队列里,工作线程在执行完任务后会再向任务队列里取出任务来执行。     

       threadFactory:创建执行任务的工作线程的线程工厂。     

       handler:拒绝任务加入线程池的策略,当线程池里的线程已经达到最大数后,再向线程池里加派任务时,线程池会拒绝执行这些任务,handler就是具体执行拒绝的对象。

线程池的大体工作思路:

       (1).当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。 

       (2).当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行 

       (3).当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务 

       (4).当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理 

       (5).当线程池中超过corePoolSize数的线程,空闲时间达到keepAliveTime时,关闭空闲线程 

       (6).当设置allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime也将关闭 

线程池的执行流程:

       任务被提交到线程池,会先判断当前线程数量是否小于corePoolSize,如果小于则创建线程来执行提交的任务,否则将任务放入workQueue队列,如果workQueue满了,则判断当前线程数量是否小于maximumPoolSize,如果小于则创建线程执行任务,否则就会调用handler,以表示线程池拒绝接收任务。

线程池池五中创建方式:

(1)1SingleThreadExecutor : 只有一个线程的线程池,因此所有提交的任务是顺序执行

代码: Executors.newSingleThreadExecutor()

(2) Cached Thread Pool : 线程池里有很多线程需要同时执行,老的可用线程将被新的任务触发重新执行,如果线程超过60秒内没执行,那么将被终止并从池中删除,

代码:Executors.newCachedThreadPool()

(3)FixedThread Pool: 拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待,

代码: Executors.newFixedThreadPool(4)

     在构造函数中的参数4是线程池的大小,你可以随意设置,也可以和cpu的数量保持一致,获取cpu的数量int cpuNums =Runtime.getRuntime().availableProcessors();

(4)ScheduledThreadPool : 用来调度即将执行的任务的线程池,

代码:Executors.newScheduledThreadPool()

(5)SingleThread Scheduled Pool : 只有一个线程,用来调度执行将来的任务,

代码:Executors.newSingleThreadScheduledExecutor()

三. 线程池方法源码解读

(1)参数解读 

/*
     *可以将这个参数看成是一个三十二位的二进制数,
     *其中前三位表示线程池的状态,
     *后二十九位表示线程池中工作线程的数量
     */
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //CAPACITY值为:00011111111111111111111111111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
     
    //RUNNING状态表示线程池可以接受任务正常工作
    private static final int RUNNING    = -1 << COUNT_BITS;
    //SHUTDOWN状态表示线程池不接受任务,但如果阻塞队列中还有任务,会将阻塞队列中的任务执行完
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //STOP状态表示线程池不接受任务,也不会执行阻塞队列中的任务,即使阻塞队列中还存在任务
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
     
    //获取线程池的状态,一般会用形参rs表示,在后面rs参数一般表示线程池状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    //获取线程池工作线程的数量,一般用形参wc表示
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

(2)execute方法

       execute方法是线程池执行任务的入口,execute方法的入参是必须要实现了Runnable接口的实现类

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        /*
         *获取线程池中工作线程的数量和核心线程数做比较
         *如果小于就调用addWorker方法,
         *
         *如果失败了会重新获取工作线程数量和线程池状态,成功就直接返回
         **/
        if (workerCountOf(c) < corePoolSize) {
            /*
             * 此时调用addWorker方法就是创建一个新的工作线程来执行这个任务
             * 向addWorker方法传入参数true,会在addWorker方法里面继续将工作线程数量和核心线程数做比较。
             */
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        /*
         * 程序运行到这里的前提条件有两个
         * 1.工作线程数量大于核心线程数量
         * 2.调用addWorker方法失败
         * 如果线程池是running状态,而且任务加入阻塞队列成功,执行if了里面的代码
         * if里面的代码其实是对加入到阻塞队列的任务的条件的一个后续判断,这句话比较绕
         * 意思是将任务放到阻塞队列了后,我还要判断是否应该将这个队列放到阻塞队列中
         * 这里为什么后续还要加判断我也不是很懂。干嘛不加在前面呢?
         */
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            /*
             * 重新判断线程池的工作状态,如果不是running状态,就将任务从队列中移除
             * 移除后调用线程池的拒绝策越,默认的是抛出异常
             */
            if (! isRunning(recheck) && remove(command))
                reject(command);
            /*
             * 执行到这的前提条件:线程池的状态是running
             * 如果将任务加入到了队列中,却发现线程池中已经没有工作线程了怎么办?
             * 我刚开始对线程池中没有工作线程还存在疑问,当allowCoreThreadTimeout设置成true的时候
             * 核心线程有可能在空闲时间超时后被回收,所以线程池存在工作线程数为0的情况
             * ,或者核心线程数设置为0也会出现这个情况
             */
            else if (workerCountOf(recheck) == 0)
                /*
                 * 传false将会在addWorker方法中判断线程池的工作线程数量和最大线程数量做比较
                 * 传一个空的任务,开启一个工作线程,但这个工作线程会发现当前的任务是空,然后会去队列中取任务
                 * 这样就避免了线程池的状态是running,而且队列中还有任务,但线程池却不执行队列中的任务
                 */
                addWorker(null, false);
        }
        /*
         * 程序执行到这的前提条件有两个:
         * 1.线程池的工作状态不是running
         * 2.任务加入到队列失败了
         * 如果是第一种情况线程池的工作状态不是running了,那调用addWorker方法也会返回false,
      0   * 就会调用拒绝执行任务的策越
         * 如果是第二种情况,加入队列失败,这说明队列已经满了,那调用addWorker方法,参数传fasle
         * 表示会将线程池的工作线程数量和最大线程数量比较,如果小于就会创建新的工作线程来执行这个任务
         */
        else if (!addWorker(command, false))
            reject(command);
    }

        execute方法比较绕,不是很直观,因为它的判断条件有点多,我感觉写法比较怪异。在execute方法中没有看到要执行任务的地方,在这个方法里只是做了条件判断,如果满足条件的话,就交给addWorker处理,并且根据判断条件的不同,传给addWorker方法的参数也不同。总结一下execute方法的执行逻辑

    1)当工作线程数量小于核心线程数量的时候,会将任务交给addWorker方法,addWorker方法会创建新的线程来处理这个任务

    2)当工作线程数量大于和核心线程数量并且线程池的工作状态是running的时候,会将任务放入到阻塞队列中

    3)当阻塞队列已经满了,会将任务交给addWorker方法处理

    4)当交给addWorker方法处理失败或是线程池的状态不是running的时候,会调用线程池的拒绝策越。

    execute方法其实是线程池工作的一个大体思路,具体细节我们可以看看addWorker方法。

(3)addWorker方法

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
     
            /*
             * 可以将if条件里面的判断条件这样看:
             * rs >= SHUTDOWN &&(rs != SHUTDOWN ||firstTask != null || workQueue.isEmpty())
             * 所以在这里能进if,让addWorker返回false的情况有这样几种
             * 1.当线程池的状态是stop
             * 2.当线程池的状态是shutdown的话,firstTask不为空
             * 3.当线程池的状态是shutdown的话,队列是空的
             * 以上三种情况返回false
             */
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;
     
            for (;;) {
                int wc = workerCountOf(c);
                /*
                 * 当传回core传true的时候,比较当前线程池工作线程数和核心线程数做比较
                 * 当传回core传false的时候,比较当前线程池工作线程数和最大线程数做比较
                 * 如果当前线程数都是大于等于他们的,直接返回false
                 */
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //利用cas函数增加线程池工作线程数,如果成功就直接跳出这两层循环
                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
            }
        }
     
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            final ReentrantLock mainLock = this.mainLock;
            //Worker是线程池的一个内部类,其实完成任务和从队列中取任务都是在Worker中完成的
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                mainLock.lock();
                try {
                    int c = ctl.get();
                    int rs = runStateOf(c);
                     
                    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();
                }
                //当将任务放到任务队列(不同于阻塞队列)成功后,启动工作线程,执行firstTask任务
     
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    
       可以看到任务的启动是在addWorker方法是生成一个新的工作线程来开启任务。Worker就将工作线程和任务封装到了自己内部,我们可以将Worker看成就是一个工作线程,至于Worker是如何执行任务和从阻塞队列中取任务,那就是Worker的事了

(4)内部类Worker代码


    /**
     * Class Worker mainly maintains interrupt control state for
     * threads running tasks, along with other minor bookkeeping.
     * This class opportunistically extends AbstractQueuedSynchronizer
     * to simplify acquiring and releasing a lock surrounding each
     * task execution.  This protects against interrupts that are
     * intended to wake up a worker thread waiting for a task from
     * instead interrupting a task being run.  We implement a simple
     * non-reentrant mutual exclusion lock rather than use
     * ReentrantLock because we do not want worker tasks to be able to
     * reacquire the lock when they invoke pool control methods like
     * setCorePoolSize.  Additionally, to suppress interrupts until
     * the thread actually starts running tasks, we initialize lock
     * state to a negative value, and clear it upon start (in
     * runWorker).
     */
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        // ...
     
        /** 工作线程 */
        final Thread thread;
        /**任务 */
        Runnable firstTask;
     
        /**
         * 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);
        }
     
        // ...
    }
      这里我只贴出了 Worker类中和本次分析有关的代码, 其他代码就省略掉了. Worker类内部还包含一个线程对象 thread 和一个 Runnable对象 firstTask. 而这个线程对象的创建过程见35行, 将Worker对象自身作为这个线程对象 thread的 Runnable参数传递给 thread的构造方法. 那么我们就可以知道, 如果要运行该线程, 也就是执行 thread.start(), 那么实际上就是要执行 thread所在的 Worker类中的 run()方法, 而从第40行又可以知道, Worker类中的 run()方法又是调用 runWorker(this); 这个方法的, 并将 thread所在的 Worker对象作为这个 runWorker()方法的参数. 简单来说就是:

        启动一个 Worker对象中包含的线程 thread, 就相当于要执行 runWorker()方法, 并将该 Worker对象作为该方法的参数. 

(5)runWorker方法 

/*
     * 这个方法是执行任务,当前任务执行完后会向队列里面取任务,
     * 如果队列里也没任务了,就会将这个工作线程从工作线程队列中移除
     * 所以线程池几个线程执行多个任务就在这里体现了,而不是
     * 一个线程执行一个任务
     */
    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) {
                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) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    //如果没有继承ThreadPoolExecutor实现这个方法,这个方法是没有执行动作的
                    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 {
                        //如果没有继承ThreadPoolExecutor实现这个方法,这个方法是没有执行动作的
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            //走到这里说明队列里的任务都已经被执行完,移除工作线程
            processWorkerExit(w, completedAbruptly);
        }
    }

    (6)getTask方法 

import java.util.concurrent.TimeUnit;
     
    /*
     * 从阻塞队列中取任务,取任务有三种情况发生
     * 1.渠道任务并返回任务
     * 2.没有取到任务,返回null,这个工作线程被回收
     * 3.没有取到任务,阻塞在向阻塞队列取任务这里
     * 第三点就是线程池中的空闲任务是如何存在的
     */
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
     
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
     
            //当线程池的工作状态是stop,就减少工作线程数,返回null
            //当线程池的工作状态是SHUTDOWN并且队列是空的时候,就减少工作线程数,返回null
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
     
            boolean timed;      // Are workers subject to culling?
     
            for (;;) {
                int wc = workerCountOf(c);
                /*
                 * 当允许核心线程超时后被收回或者是工作线程数大于核心线程数
                 * 这两种情况下都是一定要回收工作线程的
                 */
                timed = allowCoreThreadTimeOut || wc > corePoolSize;
                /*
                 * 第一次执行这个语句的时候,是一定会进if里面,跳出里面这层循环的,因为初始化的timedOut=false
                 * 当不进入这个循环,说明工作线程超时了,工作线程超时一般会返回null;
                 */
                if (wc <= maximumPoolSize && ! (timedOut && timed))
                    break;
                /*
                 * 说明工作线程超时了,工作线程超时一般会返回null;
                 *  减少工作线程数量
                 */
                if (compareAndDecrementWorkerCount(c))
                    return null;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
     
            try {
                /*
                 * timed为true的时候,当工作线程取任务超时就会返回,返回后会被回收
                 * 当timed为false的时候,说明当前工作线程不需要被回收,所以就可以在向阻塞队列取任务的时候被阻塞
                 * 这里就提现了线程池中空闲的线程是如何存在的
                 */
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                /*
                 * 走到这里,r一定为空,最后会进入到第40行的if里面,还是返回null
                 * 当返回null,这个工作线程就会被回收
                 * 
                 */
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
(7)processWorkerExit方法 

/**
     * 减少工作线程数,将工作线程从工作线程队列中移除
     */
    private void processWorkerExit(Worker w, boolean completedAbruptly) {
            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)) {
                if (!completedAbruptly) {
                    int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                    if (min == 0 && ! workQueue.isEmpty())
                        min = 1;
                    if (workerCountOf(c) >= min)
                        return; // replacement not needed
                }
                addWorker(null, false);
            }
        }
       通过源码,不仅知道了线程池的工作原理,而且知道了工作线程是如何执行任务的,当工作线程空闲的时候,线程池是如何处理的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值