ThreadPoolExecutor.Worker内部类分析

来源:互联网 发布:算法心得 epub 编辑:程序博客网 时间:2024/06/05 17:44
这篇格式较乱,重新整理了下格式,链接:http://blog.csdn.net/wangjiang87/article/details/78672684
上一篇文章分析了ThreadPoolExecutor线程池类,明白了池中的线程和任务队列是如何工作的,文章链接:http://blog.csdn.net/wangjiang87/article/details/78357218,这篇文章分析下Worker内部类来了解下池中的线程数量是如何逆向减少的。
Worker是实现了Runnable接口,主要的成员变量:
Runnable firstTask:执行run方法中的循环中第一个要执行的任务,有可能为null
Thread thread:work就是执行在这个线程当中。
这个变量可以结合addIfUnderCorePoolSize和addThread两个方法去了解。如下:
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;
}
首先,addIfUnderCorePoolSize方法中会调用addThread方法获得一个线程,然后启动这个线程,那获得的线程是哪个呢?看addThread方法。步骤如下:
(1)Worker w = new Worker(firstTask) 新建一个Worker内部类实例对象,入参是Runnable,这个Runnable也就是我们需要线程池帮我们执行的任务,把这个任务赋值给Worker内部类中的firstTask变量;
(2)Thread t = threadFactory.newThread(w);创建一个需要执行worker内部类的一个线程,别忘了woker内部类本身也实现了Runnable接口
(3)将第二步创建的线程引用赋值给内部类中的thread变量,使worker和线程关联起来,以后缓存的是worker实例对象,从而也就将线程缓存了起来
(4)将worker放到ThreadPoolExecutor的workers中缓存起来,也就是将线程放到池中
(5)poolSize加1,记录池中的线程数量
(6)上面5步执行完相当于addThread方法执行结束,将(2)中创建的线程对象返回给addIfUnderCorePoolSize方法,在addIfUnderCorePoolSize中调用t.start()启动池中的线程来执行任务。

t.start()启动之后,执行的是哪个实例的run方法呢?由第(2)步可以看出,是worker实例的,那就接着看worker内部类中的run方法的实现:
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
run方法就是一个循环去执行任务的过程,先执行第一次创建worker实例时传入的任务,执行完之后,从workQueue任务队列中取任务继续执行,直到队列中任务为空,然后执行workerDone方法。
runTask方法很简单,就是执行了task.run()方法,我们主要来看下getTask和workerDone方法。
先看getTask方法:
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
}
}
}
getTask方法主要是从workQueue中取任务,然后执行。取任务也分了三种情况:(1)state == SHUTDOWN;(2)poolSize > corePoolSize || allowCoreThreadTimeOut;(3)其他情况。
前两种使用poll方法,不阻塞或阻塞指定时间,有就返回,没有就返回null,第三种使用了take方法,队列中没有任务了就阻塞,直到有任务了才返回。
第一种情况:当state == SHUTDOWN时,也就是执行了shutDown方法之后,线程池的runState变成了关闭状态,这时候就从队列里直接取任务,队列里没有任务就直接返回null,也不阻塞等待队里里再有新元素,池中的所有线程都忽略超时时间,目的就是赶紧执行完队列里的任务,好关闭线程池;
第二种情况:poolSize > corePoolSize || allowCoreThreadTimeOut 当池中的线程数量大于核心线程数或者核心线程也要求有过期时间时,从队列中获取任务时,超过超时时间没有获取到任务,则返回null,目的就是让超过corePoolSize的线程或者是有核心线程超时时间也生效的核心线程这类线程如果空闲了指定时间,就结束生命周期,使池中的线程数量达到核心线程(若核心线程超时时间生效时即allowCoreThreadTimeOut==true,没有任务时,池中线程数量最终会变成0)
第三种情况:当前两种情况都不是时,就使用take方法阻塞,直到队列中有任务时再返回。可以想象到,如果队列中没有元素,池中的所有核心线程都会在它的take方法处阻塞。

getTask方法分析完,也就清楚了worker内部类中的run方法循环结束的条件了:就是getTask方法获取任务的前两种情况下,会结束循环,从而执行到finally块中的workerDone方法。

workDone方法:
void workerDone(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
if (--poolSize == 0)
tryTerminate();
} finally {
mainLock.unlock();
}
}
主要完成的事情有:(1)记录完成的总任务数;(2)将线程从workers缓存中移除;(3)poolSize减1

分析完Worker内部类,也就知道了线程数量是怎么从maximumPoolSize减少到corePoolSize数量的线程的逆向流程的。
原创粉丝点击