最近一直在学习java concurrent包中的相关源码,ReentrantLock、BlockQueue等,之前就一直比较困惑java线程池里的线程怎么一直运行任务的,在此记录下ThreadPoolExecutor学习笔记,如描述有误,还请多多指正。
平时大家用Executors new出来的各种线程池基本上都是基于ThreadPoolExecutor实现的,只是采用了不同的阻塞队列以及核心队列数、空闲时间等初始化参数不同罢了,接下来我们看下ThreadPoolExecutor的具体实现。
首先ThreadPoolExecutor里面一个比较晦涩的是线程池的状态及工作线程数的管理,采用了一个AtomicInteger对象ctl
进行相关的位运算巧妙的实现,源码如下,大家看的时候稍微注意下java是采用补码进行运算的,然后仔细看下源码大概就能明白了:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
//最大线程池容量2^29-1
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池对应的各种状态值
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;
// 线程池状态及工作线程数计算函数
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
然后我们解释下ThreadPoolThread几个关键的参数,初始化线程池时,通过调整这几个参数便能得到不同的线程池:
-
corePoolSize
:核心线程数,即最小的keep alive线程数,如果allowCoreThreadTimeOut
设置为true,则该参数无效,即为0; -
maximumPoolSize
:最大线程数,即定义了线程池的最大线程数(实际最大值不能超过CAPACITY
); -
keepAliveTime
:空闲时间,即线程的最大空闲时间,默认情况是当线程池中的线程数超过corePoolSize
时,线程的最大空闲时间,及线程数小于corePoolSize
时不生效,除非allowCoreThreadTimeOut
设置为true; -
workQueue
:任务队列,为阻塞队列,保存需要执行的任务。
然后我们看下工作线程的封装类Worker,该类保存了一个工作线程Thread对象并继承AbstractQueuedSynchronizer类,继承该类的作用主要在于关闭线程池方法里面shutdown方法需要获取锁才能中断线程,即正在处理任务的线程不能被中断,这个稍后在说;同时该类也实现了Runnable接口:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/** 工作线程对象 */
final Thread thread;
/** worker新建时的初始任务 */
Runnable firstTask;
/** 工作线程完成的任务数*/
volatile long completedTasks;
/**
* 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);
}
接下来我们看下ThreadPoolExecutor主要的几个方法,首先是execute
方法,通过该方法我们能大概看明白ThreadPoolExecutor对于新任务的处理流程:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
从源码可以看出,对于提交的任务,ThreadPoolExecutor分了四步处理:
- 如果当前工作线程数小于核心线程数
corePoolSize
,则直接新建一个worker处理,即新起一个线程处理,否则进入下一步; - 如果线程池处于运行状态,则将任务放入队列中等待运行,如果放入队列失败则进入下一步;
- 如果当前线程数小于
maximumPoolSize
,则新起一个线程处理,否则进入下一步; - 拒绝该任务。
流程.jpg
addWorker
方法很简单,就是新建并启动线程执行任务,但其中有个线程池状态判断的语句稍微有点难懂,我们看下源码:
//rs > SHUTDOWN || (rs==SHUTDOWN && (firstTask != null || workQueue.isEmpty)
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
这段代码根据当前线程池的状态来判断是否需要新建线程,主要为了配合shutdown及shutdownNow方法的使用,逻辑就是如果当前线程状态满足以下情况之一,那么就不新建线程,相当于不接受新任务:
- 当前线程状态>SHUTDOWN(已被stop);
- 当前线程状态==SHUTDOWN(调用了shutdown方法),并且任务不为空或者任务队列为空。
那每个线程启动后都具体在做什么呢,我们看下具体的运行方法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 {
while (task != null || (task = getTask()) != null) {//如果是刚提交的任务或者从任务队列中获取到了任务
w.lock();
//如果线程池停止,中断该线程
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
方法基本逻辑很简单,就是不断从任务队列中获取任务并执行,如果获取的任务为null,那么该线程就退出了;所以主要在于getTask
方法的实现,我们看下getTask
方法的源码:
private Runnable getTask() {
boolean timedOut = false; // 超时标识,配合keepAliveTime使用
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// rs >= STOP || (rs == SHUTDOWN && workQueue.isEmpty())
//相当于如果调用了shutdownNow,则当前线程直接退出,不管队列里面是否还有任务;如果调用了shutdown,则会继续消费队列里面的任务,直到队列为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 当前线程是否有keepAlive限制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();//这里采用阻塞队列的poll方法实现了线程的keepAliveTime,可被中断
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
那么线程池是怎么由RUNNING状态状态到SHUTDOWN以及最终状态TERMINATED的呢,ThreadPoolExecutor提供了shutdown及shutdownNow方法,shutdown方法会先将线程池状态修改为SHUTDOWN,然后中断所有idle的线程(正在 执行任务的线程不会被中断),直到所有任务执行完毕线程退出,并最终修改线程状态为TERMINATED;而shutdownNow方法则是先修改线程池状态为STOP,并中断所有线程,并最终修改线程状态为TERMINATED;从getTask
方法里面我们看到,当线程池状态值>=STOP后就不消费任务队列了,所以任务队列里面的任务将不会执行,shutdownNow会返回一个保存未执行任务的List,源码如下:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();//校验是否有关闭线程的权限
advanceRunState(SHUTDOWN);//设置线程池状态为SHUTDOWN
interruptIdleWorkers();//中断空闲线程,即还没获取到任务的线程
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();//不断尝试修改线程池为最终TERMINATED状态
}
public List shutdownNow() {
List tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();//校验是否有关闭线程权限
advanceRunState(STOP);//将线程池状态修改为STOP
interruptWorkers();//中断所有线程
tasks = drainQueue();//获取队列中未执行的任务
} finally {
mainLock.unlock();
}
tryTerminate();//不断尝试修改线程池为最终TERMINATED状态
return tasks;
}
以上差不多就是ThreadPoolExecutor的基本原理了,最后我们再看下newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool这些线程池是怎么实现的:
1、newFixedThreadPool的实现很简单,将corePoolSize、maximumPoolSize设置为固定值,队列采用的无边界队列LinkedBlockingQueue,源码如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
2、newCachedThreadPool实现的核心在于使用了SynchronousQueue队列,实现当有空闲线程时,复用空闲线程处理任务,没有空闲线程时,新起线程处理任务,同时空闲线程keepaliveTime为60秒,关于SynchronousQueue队列的原理请参看这篇文章http://www.cnblogs.com/leesf456/p/5560362.html ,newCachedThreadPool的源码如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
3、newScheduledThreadPool一般可用来实现简单的延时线程池或者定时调度线程池,实现的原理在于采用堆实现了一个优先队列,用数组实现,总是剩余延时时间最小的任务最先出队列,出队列代表该任务延时时间已到,该任务被执行;如果仅仅用于做延时线程池,那么每个任务被执行完后该任务被丢弃了,但如果用于定时调度线程池,则任务被执行完后又会重新计算延时时间并重新放入队列里面等待被执行,newScheduledThreadPool大致实现源码如下:
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
从该类的继承关系及构造函数可以函数该类继承至ThreadPoolExecutor,并且采用DelayedWorkQueue作为阻塞队列,该队列就是采用堆实现了一个优先队列,剩余延时时间最小的任务最先出队列被执行,主要看下该队列的take及offer方法:
public RunnableScheduledFuture take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture first = queue[0];//总是取队首元素
if (first == null)
available.await();//如果队列为空,则park该线程
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)//如果该任务延时时间已到,则出队列被执行
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)//此处代表如果队首元素(即剩余时间最小的任务)正处于等待中,还没有被执行,则当前线程被park
available.await();
else {//否则当前线程等待delay时长,等队首任务剩余延时时间<=0
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);//如果有新任务入队列,并且新任务的剩余延时时间最小,那么该线程会首先被唤醒,因为队列已重新排序
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture e = (RunnableScheduledFuture)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = size;
if (i >= queue.length)//如果数组容量不足,则扩容数组
grow();
size = i + 1;
if (i == 0) {
queue[0] = e;
setIndex(e, 0);
} else {
siftUp(i, e);
}
if (queue[0] == e) {//如果新任务的剩余延时时间最小,则唤醒available等待线程链表中的第一元素,相当与最小阻塞时间减小了
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
END