简洁之美-java5线程池源码赏析(上)
by davy
由于工作中很少涉及多线程编程,我一直对java的多线程懵懵懂懂。最近突遇多线程的工作,故学习之。一时兴起,查看了java5自带的线程池的部分源码,深感震撼,恐忘却,故为此文以记之。网路上对java5的线程池讨论不少,但多数未能阐述其如何复用线程,本文拟弥补缺憾,重点叙述之。
java.util.concurrent. ThreadPoolExecutor即为线程池的实现,介绍一下这个类的几个重要的成员:
(1)private final BlockingQueue<Runnable> workQueue,线程池的缓冲队列,当前的线程不足以应付待处理的任务时,将任务放入缓冲队列。
(2)private volatile int corePoolSize,线程池保持的线程数
(3)private volatile int maximumPoolSize,线程池允许的最大线程数
(4)private volatile int poolSize,线程池当前线程数
线程池的使用者只需要调用ThreadPoolExecutor的execute(Runnable command),其代码如下:
publicvoid execute(Runnable command)
{
if (command == null) thrownew NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))
{
if (runState == RUNNING && workQueue.offer(command))
{
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
elseif (!addIfUnderMaximumPoolSize(command))
{
reject(command); // is shutdown or saturated
}
}
}
这些代码足够简洁,但暗藏玄机,我们简单阐述一下。
如果execute(null),则会抛出一个空指针异常。接下来重点关注if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))语句,这句表达的是如果线程池的当前线程数超过了线程池保持的线程数,或者(小于保持的线程数时)新建线程失败的情况。简要分析一下.
(1)当程序刚启动时,poolSize=0,corePoolSize是我们初始化线程池时设置的,几乎一定>0,这时,程序肯定会执行addIfUnderCorePoolSize(command)(这个方法具体的实现后文讨论)方法为当前要处理的任务新建立一个线程并返回True,这会导致线程池里的线程数poolSize不断增加,并慢慢等于且超过可保持的线程数corePoolSize.这个过程就是新建线程,使线程数由0慢慢增长到可保持的线程数的过程。
(2)随着当前线程数poolSize增加,某时刻,poolSize会等于可保持的线程数corePoolSize,这时if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))语句条件得到满足(注:addIfUnderCorePoolSize(command)不会被执行,会被跳过,逻辑),进入if逻辑。
(3)if (runState == RUNNING && workQueue.offer(command))被执行,如果是运行状态(此时的线程数等于可保持的线程数),就把待处理的任务放入缓冲队列,并判断放入队列是否成功。这个过程就是实际线程数等于可保持的线程数,暂时就不创建线程了,把新任务放入缓冲队列里。接着,检查一下放入队列操作是否成功了,如果放入队列成功了,还要检查一下线程池的状态,确保队列里的任务能被处理掉(这是通过ensureQueuedTaskHandled(command)来完成的)。如果把任务放入缓冲队列操作失败了(比如有界队列满了等),程序会执行elseif (!addIfUnderMaximumPoolSize(command)),请见(4)。
(4)程序执行elseif (!addIfUnderMaximumPoolSize(command)){},这个很好理解,这个过程是新建立线程,使线程数量由可保持的线程数量慢慢增加到线程池所允许的最大线程数量,然后,然后就没有然后了,线程数不能再增长了。那再有新任务怎么办呢?会被 reject(command);其实就是拒绝掉,如何拒绝,拒绝后怎么办是可以自己实现的。
到这里,相信大家对线程池的控制实现都应该比较了解了。可是线程究竟是如何被复用的呢?请继续关注简洁之美-java5线程池源码赏析(下)。