深入学习JDK 线程池(之五)

来源:互联网 发布:神仙道 源码 编辑:程序博客网 时间:2024/05/21 17:46

三、FixedThreadPool线程池流程分析

      1、看上文可知,FixedThreadPool与CachedThreadPool只是参数不同,其他的都类似,现在我们就来看看不同的参数是怎么样造成不同结果的。

      先回顾一下该线程池的参数:corePoolSize为nThreads,maximumPoolSize为nThreads,BlockingQueue为LinkedBlockingQueue<Runnable>实例;

      回到execute(),addIfUnderCorePoolSize(),addThread()等几个核心方法上:

    public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {            if (runState == RUNNING && workQueue.offer(command)) {                if (runState != RUNNING || poolSize == 0)                    ensureQueuedTaskHandled(command);            }            else if (!addIfUnderMaximumPoolSize(command))                reject(command); // is shutdown or saturated        }    }

 

    private boolean addIfUnderCorePoolSize(Runnable firstTask) {        Thread t = null;        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            if (poolSize < corePoolSize && runState == RUNNING)                t = addThread(firstTask);        } finally {            mainLock.unlock();        }        if (t == null)            return false;        t.start();        return true;    }

 

    private Thread addThread(Runnable firstTask) {        Worker w = new Worker(firstTask);        Thread t = threadFactory.newThread(w);        if (t != null) {            w.thread = t;            workers.add(w);            int nt = ++poolSize;            if (nt > largestPoolSize)                largestPoolSize = nt;        }        return t;    }


        根据代码的流程可知:

        1)、该线程池接收第一个任务对象时,poolSize为0,而corePoolSize为nThreads,poolSize >= corePoolSize此时为false,所以会进行第二个条件判断,即进入addIfUnderCorePoolSize()方法,直至调用addThread()方法,运行该流程的一个非常直接的结果是poolSize会自增1,实质的效果是线程池在扩容。注意此时整体条件(poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))为false,扩容完成后,execute()方法就结束了

        2)、若线程池继续接收线程对象,重复第一步执行,直到poolSize == corePoolSize时,由于判断的短路设计,将不再调用addIfUnderCorePoolSize(),此时线程池的容量为nThreads,已经不再扩容了。注意这时候整体条件(poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))为true,将进入下一个if判断。

        由此可知:“FixedThreadPool一开始创建了数量为nThreads的线程,后续也不创建新的线程对象”,就是这样实现的。

        3)、当线程池继续接收线程对象时,由于整体条件返回为true,将进入“runState == RUNNING && workQueue.offer(command)”条件判断,现在主要是看workQueue.offer(command)的返回结果了,注意该线程池选用的BlockingQueue为LinkedBlockingQueue<Runnable>实例,这个特性非常重要,它返回的结果为true,跟CachedThreadPool选用的SynchronousQueue<Runnable>实例有很大差别。执行到这儿就意味着后来接收到的线程对象是通过LinkedBlockingQueue<Runnable>集合传递到Worker内部类的方法中去执行了,并且执行的顺序和插入的顺序一样,即FIFO(first in first out),按先后顺序执行。

       关于Worker内部类的执行可以查阅上文。

     

       2、CachedThreadPool和FixedThreadPool线程池Execute流程的区别

       1)、CachedThreadPool:由于poolSize >= corePoolSize 永远为true,所以该线程池的addIfUnderCorePoolSize()永远都不会被执行(短路判断原理)。

       2)、CachedThreadPool线程池的扩容和执行主要靠的是addIfUnderMaximumPoolSize()方法,边扩容边执行。

       3)、当CachedThreadPool池中有线程任务完成,但还没有被回收时,新接收的线程任务执行到workQueue.offer()方法时,会返回true,然后直接唤醒线程池内的worker对象执行,这样就可以利用之前创建好的worker对象,少创建一个新的线程池对象,达到重复利用的目的,减缓池的扩张速度。若没有遇到这样的时机,还是会调用addIfUnderMaximumPoolSize()方法。

       4)、FixedThreadPool线程池初始化时poolSize为0,corePoolSize为nThreads,刚开始执行时会调用addIfUnderCorePoolSize()方法,当线程池扩张到容量为nThreads时,就停止扩张,改由LinkedBlockingQueue存储新接收的线程对象,交予worker对象执行。

       5)、回顾一下Worker内部类的getTask()方法,注意第二个if判断,可以发现,CachedThreadPool走的是r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);这行代码,而FixedThreadPool则走r = workQueue.take();,workQueue的poll()方法和take()方法是有区别的,说简单点就是poll有时间限制,超时不候,结果会导致该线程对象被回收,而take()则能够等到天荒地老,该线程就一直不会被回收。所以为什么FixedThreadPool创建的线程池对象不会被回收,就是这里的原因。

 

Runnable getTask() {        for (;;) {            try {                int state = runState;                if (state > SHUTDOWN)                    return null;                Runnable r;                if (state == SHUTDOWN)  // Help drain queue                    r = workQueue.poll();                else if (poolSize > corePoolSize || allowCoreThreadTimeOut)                    r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);                else                    r = workQueue.take();                if (r != null)                    return r;                if (workerCanExit()) {                    if (runState >= SHUTDOWN) // Wake up others                        interruptIdleWorkers();                    return null;                }                // Else retry            } catch (InterruptedException ie) {                // On interruption, re-check runState            }        }    }

 


四、SingleThreadExecutor线程池

       虽然他有个装饰类FinalizableDelegatedExecutorService,说白了只是包装了nThreads为1的FixedThreadPool,流程与上面相同,略过。


 

1 0
原创粉丝点击