线程池与ThreadPoolExecutor学习笔记

来源:互联网 发布:普奇神父知乎 编辑:程序博客网 时间:2024/06/05 04:36

       学习和使用Java的时间也不算短了,一直想找个机会好好学习线程池相关的技术细节,最近正好比较闲,因此抽空来把JDK 1.7线程池的实现给研究了一下。
       线程池技术,不管是对于服务器端开发还是客户端开发都很重要。线程池最大的好处在于减少对象的创建和销毁带来的资源消耗。在面向对象程序编程中,创建和销毁对象是很消耗资源的。对于Java来说,更是如此,Java虚拟机会跟踪每一个对象,以便在对象被销毁后进行垃圾回收,这就是一些“池化技术”产生的原因。服务器端开发我不太熟悉,对于客户端开发来说。经常需要在非UI线程处理一些工作,如果每次都创建一个线程的话,对于CPU、内存、电池都受限制的手机来说,这显然是不可接受的。因此我们在进行Android开发时也采用线程池的技术来避免资源的消耗。由于我是Android程序猿,使用的语言是Java,因此我就使用JDK的线程池实现来讲解。
       想要学习了解一个类,首先要从它的构造函数开始学习。首先我们来看一下它的一个常见的构造函数。

/*** @param corePoolSize 线程池中维护的目标线程个数,在运行一段时间以后线程个数与corePoolSize相等* @param maximumPoolSize 线程池中中维护的最大线程个数* @param keepAliveTime 当线程池中的线程个数大于corePoolSize,并且由有处于idle状态的线程时,最大的等待下一个任务的时间* @param unit keepAliveTime的时间单元* @param workQueue 保存要运行的Runnable对象的的队列* @param threadFactory 当executor需要创建新的线程时使用的factory*/public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             threadFactory, defaultHandler);    }

       由构造函数可知,ThreadPoolExecutor可以创建一个线程池不断处理提交过来的Runnable对象,线程池在运行一段时间以后线程的个数等于corePoolSize,maximumPoolSize是线程池中最大的线程个数,当线程池中的线程个数大于corePoolSize并且由处于idle状态的线程,经过keepAliveTime之后线程会被销毁。线程池使用BlockingQueue来保存提交执行Runnable,当queue为空时,线程持续等待并被阻塞住。
       接下来说一下ThreadPoolExecutor这个类中贯穿始终的一些变量和方法,这些正是ThreadPoolExecutor类设计的巧妙之处。ctl是一个atomic integer,它负责保存线程池的运行状态以及当前线程池的worker线程个数。整个线程池的最大容量是2^29 - 1,也就说ctl的低29位用来存储worker线程的个数,ctl的高三位,用来存储线程池的状态,如RUNNING、SHUTDOWN、STOP等等。源码中关于这么设计的解释是2^29 - 1也已经是一个非常大的数了,以后再扩展的话再换成atomic long也很方便,而且由于在ThreadPoolExecutor中需要频繁查询线程池状态以及worker线程个数,而移位运算效率又是相对比较高的,因此采用这样的方式来设计。废话少说,下面直接上源码。

    //负责保存线程池状态以及worker线程数量    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));    private static final int COUNT_BITS = Integer.SIZE - 3;    //worker线程数量最大值,2^29 - 1    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    //取出ctl的高3位,获得线程池状态    private static int runStateOf(int c)     { return c & ~CAPACITY; }    //取出ctl的低29位,获得worker线程个数    private static int workerCountOf(int c)  { return c & CAPACITY; }    private static int ctlOf(int rs, int wc) { return rs | wc; }

       接下来是ThreadPoolExecutor的一些重要方法。

public void execute(Runnable command) {        //首先判空,空则抛异常        if (command == null)            throw new NullPointerException();        int c = ctl.get();        //如果worker线程个数小于corePoolSize        if (workerCountOf(c) < corePoolSize) {            //调用addWorker方法,此方法稍后解释            //大致就是新增一个线程,并把command作为此线程的第一个任务            //如果新增并且执行成功,直接返回            if (addWorker(command, true))                return;            c = ctl.get();        }        //如果worker线程个数 >= corePoolSize        //判断线程池运行状态,并且将command添加到保存任务的Queue中        if (isRunning(c) && workQueue.offer(command)) {            int recheck = ctl.get();            //再次检查线程池状态,如果挂了,则从Queue中移除command,并且关闭线程池            if (! isRunning(recheck) && remove(command))                reject(command);            //否则线程池正常运行的话,如果worker线程的个数等于0,那么则新增线程            //这也就是我工作的项目中使用的线程池corePoolSize和maximumPoolSize相同的原因            //因为线程个数到达corePoolSize之后新增线程的条件很严苛,绝大多数情况下maximumPoolSize是无用的            else if (workerCountOf(recheck) == 0)                addWorker(null, false);        }        //当线程池的状态不是running        //或者虽然处于running,但是队列已经满了,拒绝添加新任务        else if (!addWorker(command, false))            reject(command);    }

       对于execute方法的总结,(1)当线程池接收一个新的任务时,如果当前线程个数小于corePoolSize,那么直接调用addWorker方法新增线程,并且将command作为第一个任务进行执行。(2)如果当前线程个数大于等于corePoolSize,首先判断线程池运行状态,如果状态正常,然后试图将任务添加到队列中。如果线程池状态异常或者缓存队列已经满了,尝试新增worker线程,如果新增worker线程仍失败,则拒绝command。(3)线程个数大于等于corePoolSize,线程池状态正常,并且将任务成功添加到缓存队列中,那么再次检查线程池状态。如果线程池挂了,那么拒绝任务。否则再次检查状态仍正常的话,如果worker线程个数为0,那么新增worker线程。

    /**    * @param firstTask 新增线程执行的第一个runnable对象    * @paran core 是否以corePoolSize为线程个数限制    */    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()))                //状态异常则直接返回                return false;            for (;;) {            //检查worker线程数量                int wc = workerCountOf(c);                if (wc >= CAPACITY ||                    wc >= (core ? corePoolSize : maximumPoolSize))                    //状态异常则直接返回                    return false;                //增加worker线程数量                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 {            //创建新的worker对象            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();                        //往worker线程set添加对象                        workers.add(w);                        int s = workers.size();                        if (s > largestPoolSize)                            largestPoolSize = s;                        workerAdded = true;                    }                } finally {                    mainLock.unlock();                }                //如果添加worker成功,执行runnable                if (workerAdded) {                    t.start();                    workerStarted = true;                }            }        } finally {            if (! workerStarted)                addWorkerFailed(w);        }        return workerStarted;    }

       addWorker方法总结:此方法主要是完成添加worker对象的工作,添加成功之后则执行 runnable,不过 runnable有可能为空。

    final void runWorker(Worker w) {        Thread wt = Thread.currentThread();        Runnable task = w.firstTask;        w.firstTask = null;        w.unlock(); // allow interrupts        boolean completedAbruptly = true;        try {            //此方法的核心在于此,worker在addWorker 被创建之后,run方法直接被调用,而run方法又会调用至此方法。在此方法中,通过while 循环,不断调用getTask从blockingQueue中取出任务来执行,如果缓存队列为空,则线程被阻塞,直到有新的任务被放入缓存队列中            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 {                    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);        }    }

       runWorker()的方法的核心在于通过while 循环,不断调用getTask从blockingQueue中取出任务来执行,如果缓存队列为空,则线程被阻塞,直到有新的任务被放入缓存队列中。
       至此,线程池的简单学习笔记已经到此结束了,线程池对于移动端开发来说有着非常重要的意义,必须要掌握好!

0 0
原创粉丝点击