Java线程池基础传送门:Java线程池基础及AtomicInteger
Java线程池原理
除了newScheduledThreadPool,其他线程池都是基于ThreadPoolExecutor实现的。
线程池内部状态
既然是线程池,必然存在生命周期:运行中,关闭,停止等。ThreadPoolExecutor是用AtomicInteger变量ctl
前三位表示这个状态的,另外用了后29位表示线程数,最大可以支持5亿多,如果以后硬件可以支持启动这么多线程,直接将AtomicInteger改成AtomicLong就可以了。
- RUNNING:该状态的线程池正在运行中,会接收新任务,并处理阻塞队列中的任务
- SHUTDOWN:该状态的线程池不会接收新任务,但会处理阻塞队列中的任务
- STOP :该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行中的任务
- TIDYING:所有任务都已经处理完毕,线程数为0,转为为TIDYING状态之后,会调用
terminated()
回调 - TERMINATED:
terminated()
执行完毕
所有状态都是二进制表示的,而且依次递增,从而比较方便比较。比如想获取当前是否为运行状态,直接判断是否runStateOf(ctl)<SHUTDOWN就可以。
//保存线程数和线程状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
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; //0x111
private static final int SHUTDOWN = 0 << COUNT_BITS; //0x000
private static final int STOP = 1 << COUNT_BITS; //0x001
private static final int TIDYING = 2 << COUNT_BITS; //0x010
private static final int TERMINATED = 3 << COUNT_BITS; //0x011
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; } //取高三位
private static int workerCountOf(int c) { return c & CAPACITY; } //取低29位
private static int ctlOf(int rs, int wc) { return rs | wc; }
//高三位+第三位进行或操作即可
private static int ctlOf(int rs, int wc) { return rs | wc; }
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
//下面三个方法,通过CAS修改worker的数目
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
//只尝试一次,失败了则返回,是否重试由调用方决定
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
//跟上一个不一样,会一直重试
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
下面是比较核心的字段,这里workers采用的是非线程安全的HashSet,而不是线程安全的版本,主要是因为这里有一些复合的操作,比如说将worker添加到workers后,我们还需要判断是需要更新largestPoolSize等,workers只有在获取到mainLock的情况下才会进行读写,另外这里的mainLock也用于中断线程的时候进行串行执行,否则如果不加锁的话,可能会造成并发去中断线程,引起不必要的中断风暴。
private final ReentrantLock mainLock = new ReentrantLock();
private final HashSet<Worker> workers = new HashSet<Worker>();
private final Condition termination = mainLock.newCondition();
private int largestPoolSize;
private long completedTaskCount;
核心方法
ThreadPoolExecutor#submit()
拿到一个线程池后,我们就可以提交任务,让它去执行了,我们看一下submit是如何实现的。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
这两个方法都很简单,可以接收Runnable和Callable参数,都包装成RunnableFuture,然后调用execute()方法,execute是线程池最核心的一个方法。
ThreadPoolExecutor#execute()
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//判断当前worker数量,小于核心则扩容
if (workerCountOf(c) < corePoolSize) {
/*
调用addWorker(Runnable firstTask, boolean core)进行扩容,
fitstTask表示worker启动后要执行的第一个任务,core为是否为核心线程
*/
if (addWorker(command, true))
return;
//如果增加失败,那么重新获取ctl快照(有可能线程池在这个期间关闭了)
c = ctl.get();
}
//线程池状态为正在执行,然后将任务加入队列成功
if (isRunning(c) && workQueue.offer(command)) {
//
int recheck = ctl.get();
/*
进行一次double check,检查线程池是否关闭了。
如果不是运行中的状态,则不再接受新任务,将任务移除成功后调用拒绝处理器jeject()
*/
if (! isRunning(recheck) && remove(command))
reject(command); //执行reject方法处理任务
/*
如果仍为进行中的状态,则double check检查当前work是否为0,
如果为0,则可能在上面逻辑执行过程中,有worker销毁,那么再进行一次扩容。
这里任务已经进入队列了,所以不需要传递firstTask,这里的扩容为非核心线程false
*/
else if (workerCountOf(recheck) == 0) //???
addWorker(null, false); //????
}
else if (!addWorker(command, false))
reject(command);
}
……%%&……*@#@ 待补充,没看懂
ThreadPoolExecutor#addWorker()
线程池的具体扩容操作
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); //获取当前线程池的状态
/*
如果线程值的状态>=shutdown,那么此时线程池已经不再接受任务,因此可以直接返回false;
如果状态为SHUTDOWN并且firstTask为空,同时队列非空,则可以进行扩容
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
//如果work大于容量,直接返回false;然后根据是否为核心线程判断work数量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS操作自增worker,成功则跳出循环
if (compareAndIncrementWorkerCount(c))
break retry; //跳出retry循环,开始创建新的线程执行任务
// 重新读取变量
c = ctl.get();
//和之前线程池状态不一致(比如线程池关闭了),则跳到最外层for循环
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 {
w = new Worker(firstTask); //新建线程
final Thread t = w.thread;
if (t != null) {
//可重入锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); //获得锁
try {
int rs = runStateOf(ctl.get()); //获取线程运行状态
//当前线程池正在运行或者 已经shutdown并且没有正在执行的任务
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//判断线程是否已经启动,如果为true,证明已经调用了start方法,抛出异常
if (t.isAlive())
throw new IllegalThreadStateException();
//加入HashSet<worker> workers
workers.add(w);
int s = workers.size();
//更新largestPoolSize
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//如果worker添加成功,则启动线程,这时候worker就开始执行任务了
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
//添加失败
addWorkerFailed(w);
}
return workerStarted;
}
ThreadPoolExecutor#Worker
-
Worker继承自同步器AQS,可以方便的实现线程中止操作。使用了状态变量state表示是否获取锁,0表示解锁,1表示获得所。exclusiveOwnerThread表示当前持有锁的线程。
-
实现了Runnable接口,可以将自身作为一个任务在工作线程中执行。
-
在创建线程对象时,将自身作为参数传入,当调用Thread.start()时,实际上是调用task.run();
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
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); //实际执行的Run方法
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
//尝试CAS操作获取锁
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
/*
只有在线程启动后,才会进行中断操作。
*/
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
这里执行Worker构造方法时,state状态为-1,直到执行runWorker方法时才会被设置为0,也就是说,在Worker启动之前,都不允许进行加锁操。
ThreadPoolExecutor#runWorker()
在线程启动后,实际执行的Run方法是runWorker(Worker w)
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 此时允许中断正在执行的任务,将state设为0,其他线程可以获得所或者中断锁
boolean completedAbruptly = true;
try {
/*
首先尝试执行firstTask,若没有的话,则调用getTask()从队列中获取任务
*/
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
/*
以上翻译过来:
如果线程池正在停止,请确保线程被中断;
如果没有,请确保线程没有中断。
这需要在第二种情况下重新检查,以便在清除中断时处理shutdownnow race
*/
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回调函数,同上
afterExecute(task, thrown);
}
} finally {
task = null;
//已完成任务++,因为在锁中,没有线程安全问题,volatile修饰,保证可见性
w.completedTasks++;
w.unlock(); //释放锁
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); //执行出队
}
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//如果是异常终止的,那么减少worker的数目
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//将当前worker中workers中删除掉,并累加当前worker已执行的任务到completedTaskCount中
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//上文说过,减少worker的操作都需要调用这个方法
tryTerminate();
/*
如果当前线程池仍然是运行中的状态,那么就看一下是否需要新增另外一个worker替换此worker
*/
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
/*
如果是异常结束的则直接扩容,否则的话则为正常退出,比如当前队列中已经没有任务需要处理,
如果允许core线程超时的话,那么看一下当前队列是否为空,空的话则不用扩容。否则话看一下
是否少于corePoolSize个worker在运行。
*/
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
private Runnable getTask() {
boolean timedOut = false; // 上一次poll()是否超时了
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 若线程池关闭了(状态大于STOP)
// 或者线程池处于SHUTDOWN状态,但是队列为空,那么返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
/*
如果允许core线程超时 或者 不允许core线程超时但当前worker的数目大于core线程数,
那么下面的poll()则超时调用
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/*
获取任务超时了并且(当前线程池中还有不止一个worker 或者 队列中已经没有任务了),那么就尝试
减少worker的数目,若失败了则重试
*/
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();
if (r != null)
return r;
//走到这里表明,poll调用超时了
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
这里有个貌似不太起眼的方法tryTerminate
,这个方法会在所有可能导致线程池终结的地方调用,比如说减少worker的数目等,如果满足条件的话,那么将线程池转换为TERMINATED状态。另外这个方法没有用private修饰,因为ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,而ScheduledThreadPoolExecutor也会调用这个方法。
final void tryTerminate() {
for (;;) {
int c = ctl.get();
/*
如果当前线程处于运行中、TIDYING、TERMINATED状态则直接返回,运行中的没
什么好说的,后面两种状态可以说线程池已经正在终结了,另外如果处于SHUTDOWN状态,
并且workQueue非空,表明还有任务需要处理,也直接返回
*/
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//可以退出,但是线程数非0,那么就中断一个线程,从而使得关闭的信号能够传递下去,
//中断worker后,worker捕获异常后,会尝试退出,并在这里继续执行tryTerminate()方法,
//从而使得信号传递下去
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//尝试转换成TIDYING状态,执行完terminated回调之后
//会转换为TERMINATED状态,这个时候线程池已经完整关闭了,
//通过signalAll方法,唤醒所有阻塞在awaitTermination上的线程
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
/**
* 中断空闲的线程
* @param onlyOne
*/
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
//遍历所有worker,若之前没有被中断过,
//并且获取锁成功,那么就尝试中断。
//锁能够获取成功,那么表明当前worker没有在执行任务,而是在
//获取任务,因此也就达到了只中断空闲线程的目的。
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
关闭线程池
关闭线程池一般有两种形式 ,shutdown()和shutdownNow()
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//通过CAS将状态更改为SHUTDOWN,这个时候线程池不接受新任务,但会继续处理队列中的任务
advanceRunState(SHUTDOWN);
//中断所有空闲的worker,也就是说除了正在处理任务的worker,其他阻塞在getTask()上的worker
//都会被中断
interruptIdleWorkers();
//执行回调
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
//这个方法不会等待所有的任务处理完成才返回
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
/*
不同于shutdown(),会转换为STOP状态,不再处理新任务,队列中的任务也不处理,
而且会中断所有的worker,而不只是空闲的worker
*/
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();//将所有的任务从队列中弹出
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
/*
将队列中所有的任务remove掉,并添加到taskList中,
但是有些队列比较特殊,比如说DelayQueue,如果第一个任务还没到过期时间,则不会弹出,
因此这里通过调用toArray方法,然后再一个一个的remove掉
*/
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}