写在2017.09.19
最近新工作稳定些(加班还是忙成狗),某些晚上11点后还是有点自己的时间写写博客,最近对JUC中有些技术有些热情,将尝试解读下,定个小目标解读到AQS,先从用得多知道其实并不多的ThreadPoolExecutor进行解读。
本文从小的case引出为什么使用线程池,怎么使用,线程池的内部原理是什么,参数怎么设置。当然都是我个人理解,如果有异议也请指教下,相互学习。
(18/7/30有点变更,之前的博文有些地方不是太深入,本次有点改动,并且重新排版)。
话题引出
在请求-相应模式中,不使用线程池模式,有2中模式可供选择:
case1. B收到A用户请求后入队列,B启动单独任务轮询队列挨个处理,并做返回
case2. B收到A请求后,对每个请求新建一个线程处理
如果用户请求多,并发大,Case1和Case2会发生什么情况?
Case1:单任务处理,不能有效利用CPU,可能大量请求堆积在队列,导致堆积的任务超时;如果队列满了以后,怎么处理,直接拒绝?丢掉?。。。
Case2:计算机上CPU是有限的,如果请求过大,线程过大,导致线程间的切换耗时较大,系统无法在创建线程,JVM虚拟机方法栈stackOverFlowError,甚至因为大量线程创建对象,导致系统卡顿。
进一步疑问
1.每处理一个请求就要创建一个线程然后销毁,系统资源开销大,系统(CPU 内存)表示扛不住
2.线程处理时,如果遇到异常,请求就丢失了(有没有可能记录下)
3.瞬时并发请求高,并发的线程数大,系统压力大下游调用游系统也会造成较大压力
4.这么多线程,怎么优雅停止线程?使用信号量,或使用线程中断方法,太复杂了吧。
还有其他....
,
解决方案就是大家知道的容器(即pool)作为请求的存储、中转调度,重复利用线程,避免频繁创建和销毁。
1.因为池子有容量,适当的容量(线程数)起到资源的重复利用,线程的创建销毁就能得到减少
2.池子能够调节容器的大小,瞬时洪峰到来能转移到池子中系统处理能力内得到处理,避免了对系统的冲击
3.当池子满时,我们可以有多种处理方式:转移,丢弃,拒绝。。。
还应关注
1.重启时是否会丢失?池子中数据是否可以持久化的?请求序列化和反序列化的方法?
2.请求入池,线程拉取请求的策略是什么?(FIFO,FILO...)池子的并发策略?(锁,并发队列...)
3.工作线程是否会因为处理请求异常而中断? 处理失败的请求,任务降级的策略是什么?(重新入池:从队头进,从队尾进,任务降级,重试次数...)
4.请求过大,超过负荷,池子的降级策略又是怎么实现?
5.线程的优先级?
6.下面场景又要怎么解决:
(1)并发数量大,RT时间短,如淘宝,天猫的访问每天的访问达到100多亿次
(2)并发数小,RT时间长,如后台程序对几十亿买家数据进行打标,对不同的标发放优惠券并发短信
(3)并发数大,RT时间长(有问题了)
。。。(暂时想到这些,文章不能面面俱到,请见谅)
JDK中的解决方案
JDK中为以上的问题提供线程池,位于java.util.concurrent包中。
线程池的使用类型
1.Executors#newFixedThreadPool(int nThreads) 固定线程数运行
2.Executors#newSingleThreadExecutor() 单线程池
3.Executors#newCachedThreadPool() 无限大小线程池,最多可到2^29个线程。
4.Executors#newSingleThreadScheduledExecutor() 调度线程池
ThreadPoolExecutor
无论是使用何种类型的线程池,其底层均是使用ThreadPoolExecutor来实现,从ThreadPoolExecutor类可以解读到线程池状态,如何处理任务,如何停止。
线程池状态解读
线程状态
线程池状态
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;
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;
上面的代码可以看出,线程池使用ctl 作为线程的counter,ctl的高位存储表示线程池状态,其中2^29表示SHUTDOWN ,2^30表示STOP ,2^31表示TIDYING ,2^32表示TERMINATED。
状态图如下:
ThreadPoolExecutor构造函数解读
ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler)
JDK官方文档描述:
If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full
If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread
If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
【corePoolSize】----核心线程数,线程提交时,即使运行中的线程+空闲线程数
【maximumPoolSize】----只有在队列满时,线程数小于maximumPoolSize,创建新的线程,线程数>corePoolSize时在keepAliveTime时间内从队列中获取不到任务,线程将被销毁
【keepAliveTime】----见maximumPoolSize和corePoolSize的描述。
【unit】----keepAliveTime的单位时间
【workQueue】----阻塞队列,线程池达到核心线程数时入此队列,工作线程不断地从队列中获取任务,如果是allowCoreThreadTimeOut=true或工作线程数大于corePoolSize,在keepAliveTime时间内获取不到任务的线程将会销毁。队列类型见后面描述。
【threadFactory】----线程工厂,用于创建新线程(使用地方见Worker的构造方法,本文后后部分有介绍)。
【handler】----线程拒绝策略,需实现RejectedExecutionHandler接口,常见策略本文也将介绍。
workQueue类型
1.SynchronousQueue: 直接提交队列,每个 put 必须等待一个 take,配合着无界线程池(Executors#newCachedThreadPool())使用。
2.LinkedBlockingQueue:链表实现的有界队列阻塞队列,按先进先出的原则对元素进行排序(FIFO),队列满时存放元素的线程会被阻塞,队列空时会阻塞从队列中取元素的线程。默认值为Integer.MAX_VALUE(建议不使用,流量洪峰时并没有调用更多的线程去处理请求,如果峰值持续时间较长,任务会在队列里积压,可能会导致超时,甚至于所有新任务都要排队,最终所有请求超时)。这里有原理解析。
3.ArrayBlockingQueue:用数组实现的有界阻塞队列,按先进先出的原则对元素进行排序(FIFO),队列满时会增加线程到最大值。这里有原理解析。
4.PriorityBlockingQueue:无界阻塞优先级队列(原理介绍)
RejectedExecutionHandler拒绝策略
- AbortPolicy ---- 拒绝新的新请求,异常抛出
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
- CallerRunsPolicy ----直接在execute方法外运行该任务,如果线程池停止则抛弃该任务
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
- DiscardPolicy----丢弃该任务,不抛出异常
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
- DiscardOldestPolicy ---- 丢弃存在队列中本该要执行的请求,将新的请求加入队列中
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- 可以自定义——实现RejectedExecutionHandler接口,内部实现rejectedExecution(Runnable r, ThreadPoolExecutor e) 。
想了解更多可以参考:http://blog.csdn.net/pozmckaoddb/article/details/51478017
源码分析
提交线程
使用execute 和submit。
execute
当线程数小于corePoolSize时,新请求不会进入队列,会直接创建新线程处理;如果运行的线程数等于corePoolSize,且队列未满,新请求将进入队列,工作线程不断从队列中获取任务执行;队列满时,当工作线程数小于maximumPoolSize,线程池创建新线程;当工作线程数等于maximumPoolSize,且队列满,新的任务会走拒绝策略。当然在向线程池提交任务或是线程从队列中获取任务时,会检查线程池状态,如果线程池处于SHUTDOWN及以上状态时,新任务将不能提交,被拒绝;如果线程池是SHUTDOWN状态且队列是空队列或STOP及以上状态,线程销毁。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
// ctl为当前线程数量,AtomicInteger类型
int c = ctl.get();
//int workerCountOf(int c) { return c & CAPACITY; }比较简单,
//线程数量最多CAPACITY(为2^29-1)个
//当前线程数小于核心线程数
if (workerCountOf(c) < corePoolSize) {
//如果线程数小于核心线程数,则新建线程并运行,后面有详细描述
if (addWorker(command, true))
return;
c = ctl.get();
}
//线程池处于SHUTDOWN以下状态(非停止状态)且队列未满
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//如果线程池非工作状态,则尝试从队列移除,如果能移除则执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//当前线程池内线程数为0
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {//外循环
int c = ctl.get();
//ctl低位表示线程数,高位存储线程池状态:2^29表示线程池SHUTDOWN
//2^30表示STOP,2^31表示TIDYING,2^32表示TERMINATED
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//线程池状态(SHUTDOWN,TIDYING,TERMINATED),状态且是空队列,返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {//内循环
int wc = workerCountOf(c);
//以下情况返回false,不新建线程
//1.线程容量已达到2^29-1
//2.若当前线程数小于核心线程数才调用此方法时,
//再check发现前线程数大于核心线程数
//3.当前线程数 >= 最大线程数
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//对线程计数+1成功,退出外循环,继续执外循环后的操作
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//若线程池状态改变,则跳出内循环,继续在外循环执行;
//否则,继续在本内循环执行
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 {
//构造Worker(该类继承AQS和Runnable,见后文):
// 若是execute方法中是未达到核心线程数时调用,
//firstTask非null是任务,大于核心线程数时调用firstTask是null
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//互斥锁,保证同一时间只有1个调用
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//将线程存放workers(HashSet )中,
//workers中存放的是所有工作线程
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//启动线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
//启动线程失败,从workers删除,如线程池是TIDYING尝试停止线程池
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
work实现
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
。。。
Worker(Runnable firstTask) {
//设置QAS同步状态,-1表示初始状态,
// 1表示lock状态(有任务在执行中),0表示unlock(没有任务正在执行)
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
//新建Thread线程
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
//该方法轮询从队列中获取任务,并执行任务的run方法,详细见后文
runWorker(this);
}
。。。
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 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
//这里也比较简单,线程池STOP及以上状态将线程中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//任务执行前执行,无方法体,扩展使用,可以对任务做业务操作:如持久化,统计,打日志,收集线程池信息等
beforeExecute(wt, task);
Throwable thrown = null;
try {
//调用任务的run方法
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 {
//和beforeExecute实现一致,作用差不多
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
//释放锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
//线程销毁时候执行
processWorkerExit(w, completedAbruptly);
}
}
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//SHUTDOWN以上状态且(状态是STOP及以上或是空队列)时,线程数-1
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
//调用ThreadPoolExecutor#allowCoreThreadTimeOut(true)或线程数>核心线程数,
//在运行线程池队列为空时允许线程在keepAliveTime时间后销毁,从works中移除
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//从阻塞队列中获取任务。
//若存在设置核心线程数可超时销毁或线程数大于核心线程数
//时,在keepAliveTime时间内获取;否则从队列阻塞直到获取到任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//超时未获取到,设置timedOut=true
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
线程销毁时候执行,其中completedAbruptly表示因用户异常导致的线程结束
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
//从HashSet中删除线程
workers.remove(w);
} finally {
mainLock.unlock();
}
//见后文分析
tryTerminate();
int c = ctl.get();
//在RUNNING和SHUTDOWN状态时,线程池中允许的线程数min ,
//若当前线程数>min ,该线程自动销毁;否则运行在线程池和对列允
//许的情况下,新建线程来保证达到核心线程数
if (runStateLessThan(c, STOP)) {
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);
}
}
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//运行,TIDYING或TERMINATED、SHUTDOWN状态且队列不为空
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//SHUTDOWN且队列为空或STOP状态时,将Worker中所有线程执行interrupt方法
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//CAS保证同一时间只有一个线程中将线程池变成TIDYING状态
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//空方法,扩展使用,如打日志,统计,收集工作线程信息等。
terminated();
} finally {
//将线程池变成TERMINATED状态
ctl.set(ctlOf(TERMINATED, 0));
//一般停止线程池时通常会使用awaitTermination()方法,如
//果线程池未能及时停止,将wait,这里的signalAll作用类似
//notifyAll(),去唤醒等待的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
submit
与execute方法相比,submit参数除了Runnable参数还能接收Callable参数;
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
上面的代码可以看出,将提交的任务(假设task)重新构造出一个新的任务FutureTask(FutureTask继承RunnableFuture,RunnableFuture继承自Runnable和Future),并调用execute方法,线程池线程执行时将调用FutureTask的run方法(具体请看上面所述中Worker#runWorker()方法),FutureTask#run()内部又去调用提交任务的call()方法,通过RunnableFuture#get()方法几个获取call()的返回值。
同execute方法相比,submit提交的线程,无论发生异常以否,都会有返回值Future(如发生异常,异常会被吃掉,结果值会被包装在Future中),通过Future可以取消任务,获取返回值,获得执行完成的时间等。
FutureTask
public class FutureTask implements RunnableFuture {
。。。
/** The underlying callable; nulled out after running */
private Callable callable;
/** The result to return or exception to throw from get() **/
//回值存在outcome,通过get()获取
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
//若任务未执行完成,外部通过get()方法获取call()方法返回值的线程将阻塞入栈
private volatile WaitNode waiters;
。。。
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
//适配器模式,将Runnable转call(),call()方法调用任务的run()
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
。。。
public void run() {
//线程非NEW状态或使用UNSAFE设置当前的状态不能成功,返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
//再次检查,确保线程是NEW状态时执行
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
//任务设置成完成,然后将对outcome设价成result,并将waiters上的所有阻塞线程unpark
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
/**
* Ensures that any interrupt from a possible cancel(true) is only
* delivered to a task while in run or runAndReset.
*/
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
//cancel(true)调用时,如果任务还没运行,任务状态此时为
//INTERRUPTING,以下的方法会让出CPU时钟直到状态不为INTERRUPTING
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt
// assert state == INTERRUPTED;
// We want to clear any interrupt we may have received from
// cancel(true). However, it is permissible to use interrupts
// as an independent mechanism for a task to communicate with
// its caller, and there is no way to clear only the
// cancellation interrupt.
//
// Thread.interrupted();
}
//mayInterruptIfRunning=true时,对线程执行interrupt()方法,标记任务为INTERRUPTED状态
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
。。。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
。。。
}
关闭线程池
如何停止线程,相信很多人都知道使用interrupt()或信号量来优雅停止线程,但对于多个线程,这种方式显得太过麻烦;线程线程池使用shutdown()和shutdownNow()停止线程池,其中推荐使用shutdown(),较为优雅。
shutdown
将线程状态通过CAS变成SHUTDOWN状态,并将对空闲线程调用Thread#interrupt()方法,然后调用tryTerminate()方法轮询,线程池是TIDYING或TERMINATED状态终止轮询,如果是STOP或SHUTDOWN状态(且队列是空),尝试将状态变成TIDYING->TERMINATED。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//安全检查,确保调用者可以执行线程池中线程的关闭
checkShutdownAccess();
//若线程是RUNNING,变更成SHUTDOWN状态
advanceRunState(SHUTDOWN);
//空闲线程调用interrupt()方法
interruptIdleWorkers();
//空方法,留待扩展使用
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
shutdown停止线程池还是比较优雅的,但某个任务执行时间长,很有可能使线程池无法终止(使用isTerminated()可以检查线程池是否终止),甚至死循环。
如下2种停止线程的判断:如果有任务无法执行完,case1可能死循环;case2如果没有在限定时间内停掉线程池,程序可以继续执行其他操作,即使此时线程池无法停下,这点还是比较优雅的,尤其是在顺序销毁多个资源的时候。
case1
ExecutorService threadPool = ...
。。。
threadPool .shutdown();
while(!threadPool .isTerminated()){
//sleep(), &etc;
}
case2
ExecutorService threadPool = ...
。。。
threadPool .shutdown();
pool.awaitTermination(1, TimeUnit.MINUTES);
shutdownNow
将线程池变成STOP状态,对所有线程触发interrupt()方法(任务中有sleep(time),wait(),wait(time)时会发生InterruptedException,程序需要处理该异常),然后将队列中的任务移除。
不优雅:对工作线程触发Thread#interrupt(),将队列中提交的任务移除。不推荐使用,如果要使用,需要考虑任务移除影响和任务对InterruptedException的处理。
public List shutdownNow() {
List tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//若线程是RUNNING,SHUTDOWN,变更成STOP状态
advanceRunState(STOP);
//对线程池中线程执行interrupt()
interruptWorkers();
//将队列中的任务全部移除
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
线程池的设定
线程池的设定有多种方案,有粗放的,也有理论可以很难实施的,也有可计算型。
但对线程池来说:
当生产能力<消费能力时,以固定的线程数(corePoolSize)运行
当生产能力>消费能力时,要增加处理的线程数到最大值(maximumPoolSize)
经过上一步后,如果生产能力下降,要将线程数重新下降到固定的数量
最佳线程数量
服务器端最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间) * cpu数量 (CPU瓶颈类型)
粗放型
IO和CPU上考虑,是IO密集型(如http请求,数据库读写,文件读写,RPC调用等),还是CPU密集型。
IO密集型,意味着系统需要花很多时间在IO阻塞上,我们应该设置可能多的线程数,建议设置为2*CPU数。
CPU密集型务,我们应该设置数目较小的线程数,推荐使用CPU数+1(线程数目太多,线程切换所带来的开销又会对系统的响应时间带来影响)。
可计算型
进行压测,获得最大的QPS,和每个任务执行的时间。
tasks :每秒的任务数(即QPS),假设为500~1000;taskcost:每个任务花费时间,假设为0.1s;responseTime:系统允许容忍的最大响应时间,假设为1s。
corePoolSize:
吞吐量 = tasks/(1/taskcost) =taskstaskcout = (500~1000)0.1 = 50~100 个线程。corePoolSize设置应该大于50,根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可
workQueue
队列长度=(coreSizePool/taskcost)*responsetime 建议勿设置程Integer.MAX_VALUE(文中提到的流量洪峰导致超时)
maximumPoolSize
maximumPoolSize=(max(tasks)- queueCapacity)/(1/taskcost)
参考:http://www.cnblogs.com/lengender-12/p/6869554.html
http://www.cnblogs.com/waytobestcoder/p/5323130.html