新云软件园:请安心下载,绿色无病毒!

软件提交最近更新热门排行
您现在的位置:首页››编程教学››Java编程››Java技巧

java教程Java线程执行原理解析

2016-11-18 09:10作者:佚名来源:本站整理浏览:203 评论:0

线程池状态

首先认识两个贯穿线程池代码的参数:

runState:线程池运行状态
workerCount:工作线程的数量
线程池用一个32位的int来同时保存runState和workerCount,其中高3位是runState,其余29位是workerCount。代码中会反复使用runStateOf和workerCountOf来获取runState和workerCount。

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;

// 线程池状态
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操作
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; }
RUNNING:可接收新任务,可执行等待队列里的任务
SHUTDOWN:不可接收新任务,可执行等待队列里的任务
STOP:不可接收新任务,不可执行等待队列里的任务,并且尝试终止所有在运行任务
TIDYING:所有任务已经终止,执行terminated()
TERMINATED:terminated()执行完成
线程池状态默认从RUNNING开始流转,到状态TERMINATED结束,中间不需要经过每一种状态,但不能让状态回退。下面是状态变化可能的路径和变化条件:

图1 线程池状态变化路径

java教程Java线程执行原理解析

Worker的创建

线程池是由Worker类负责执行任务,Worker继承了AbstractQueuedSynchronizer,引出了Java并发框架的核心AQS。

AbstractQueuedSynchronizer,简称AQS,是Java并发包里一系列同步工具的基础实现,原理是根据状态位来控制线程的入队阻塞、出队唤醒来处理同步。
AQS不会在这里展开讨论,只需要知道Worker包装了Thread,由它去执行任务。

调用execute将会根据线程池的情况创建Worker,可以归纳出下图四种情况:

图2 worker在线程池里的四种可能

java教程Java线程执行原理解析

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();

int c = ctl.get();
//1
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
//3
reject(command);
else if (workerCountOf(recheck) == 0)
//4
addWorker(null, false);
}
//5
else if (!addWorker(command, false))
//6
reject(command);
}
标记1对应第一种情况,要留意addWorker传入了core,core=true为corePoolSize,core=false为maximumPoolSize,新增时需要检查workerCount是否超过允许的最大值。

标记2对应第二种情况,检查线程池是否在运行,并且将任务加入等待队列。标记3再检查一次线程池状态,如果线程池忽然处于非运行状态,那就将等待队列刚加的任务删掉,再交给RejectedExecutionHandler处理。标记4发现没有worker,就先补充一个空任务的worker。

标记5对应第三种情况,等待队列不能再添加任务了,调用addWorker添加一个去处理。

标记6对应第四种情况,addWorker的core传入false,返回调用失败,代表workerCount已经超出maximumPoolSize,那就交给RejectedExecutionHandler处理。

private boolean addWorker(Runnable firstTask, boolean core) {
//1
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);

// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;

for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
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
}
}
//2
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 {
// 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.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
标记1的第一段代码,目的很简单,是为workerCount加一。至于为什么代码写了这么长,是因为线程池的状态在不断变化,并发环境下需要保证变量的同步性。外循环判断线程池状态、任务非空和队列非空,内循环使用CAS机制保证workerCount正确地递增。不了解CAS可以看认识非阻塞的同步机制CAS,后续增减workerCount都会使用CAS。

标记2的第二段代码,就比较简单。创建一个新Worker对象,将Worker添加进workers里(Set集合)。成功添加后,启动worker里的线程。在finally里判断线程是否启动成功,不成功直接调用addWorkerFailed。

private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
addWorkerFailed将减少已经递增的workerCount,并且调用tryTerminate结束线程池。

Worker的执行

Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}

public void run() {
runWorker(this);
}
Worker在构造函数里采用ThreadFactory创建Thread,在run方法里调用了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 {
//1
while (task != null || (task = getTask()) != null) {
w.lock();
//2
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//3
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;
//4
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false; //5
} finally {
//6
processWorkerExit(w, completedAbruptly);
}
}
标记1进入循环,从getTask获取要执行的任务,直到返回null。这里达到了线程复用的效果,让线程处理多个任务。

标记2是一个比较复杂的判断,保证了线程池在STOP状态下线程是中断的,非STOP状态下线程没有被中断。如果你不了解Java的中断机制,看如何正确结束Java线程这篇。

标记3调用了run方法,真正执行了任务。执行前后提供了beforeExecute和afterExecute两个方法,由子类实现。

标记4里的completedTasks统计worker执行了多少任务,最后累加进completedTaskCount变量,可以调用相应方法返回一些统计信息。

标记5的变量completedAbruptly表示worker是否异常终止,执行到这里代表执行正常,后续的方法需要这个变量。

标记6调用processWorkerExit结束,后面会分析。

接着来看worker从等待队列获取任务的getTask方法:

private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?

for (;;) {
int c = ctl.get();
int rs = runStateOf(c);

//1
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}

int wc = workerCountOf(c);
//2
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
//3
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
标记1检查线程池的状态,这里就体现出SHUTDOWN和STOP的区别。如果线程池是SHUTDOWN状态,还会先处理完等待队列的任务;如果是STOP状态,就不再处理等待队列里的任务了。

标记2先看allowCoreThreadTimeOut这个变量,false时worker空闲,也不会结束;true时,如果worker空闲超过keepAliveTime,就会结束。接着是一个很复杂的判断,好难转成文字描述,自己看吧。注意一下wc>maximumPoolSize,出现这种可能是在运行中调用setMaximumPoolSize,还有wc>1,在等待队列非空时,至少保留一个worker。

标记3是从等待队列取任务的逻辑,根据timed分为等待keepAliveTime或者阻塞直到有任务。

最后来看结束worker需要执行的操作:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
//1
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();

//2
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}

//3
tryTerminate();

int c = ctl.get();
//4
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);
}
}
正常情况下,在getTask里就会将workerCount减一。标记1处用变量completedAbruptly判断worker是否异常退出,如果是,需要补充对workerCount的减一。

标记2将worker处理任务的数量累加到总数,并且在集合workers中去除。

标记3尝试终止线程池,后续会研究。

标记4处理线程池还是RUNNING或SHUTDOWN状态时,如果worker是异常结束,那么会直接addWorker。如果allowCoreThreadTimeOut=true,并且等待队列有任务,至少保留一个worker;如果allowCoreThreadTimeOut=false,workerCount不少于corePoolSize。

总结一下worker:线程池启动后,worker在池内创建,包装了提交的Runnable任务并执行,执行完就等待下一个任务,不再需要时就结束。

线程池的关闭

线程池的关闭不是一关了事,worker在池里处于不同状态,必须安排好worker的”后事”,才能真正释放线程池。ThreadPoolExecutor提供两种方法关闭线程池:

shutdown:不能再提交任务,已经提交的任务可继续运行;
shutdownNow:不能再提交任务,已经提交但未执行的任务不能运行,在运行的任务可继续运行,但会被中断,返回已经提交但未执行的任务。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess(); //1 安全策略机制
advanceRunState(SHUTDOWN); //2
interruptIdleWorkers(); //3
onShutdown(); //4 空方法,子类实现
} finally {
mainLock.unlock();
}
tryTerminate(); //5
}
shutdown将线程池切换到SHUTDOWN状态,并调用interruptIdleWorkers请求中断所有空闲的worker,最后调用tryTerminate尝试结束线程池。

public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue(); //1
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
shutdownNow和shutdown类似,将线程池切换为STOP状态,中断目标是所有worker。drainQueue会将等待队列里未执行的任务返回。

interruptIdleWorkers和interruptWorkers实现原理都是遍历workers集合,中断条件符合的worker。

上面的代码多次出现调用tryTerminate,这是一个尝试将线程池切换到TERMINATED状态的方法。

final void tryTerminate() {
for (;;) {
int c = ctl.get();
//1
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//2
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
//3
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
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
}
}
标记1检查线程池状态,下面几种情况,后续操作都没有必要,直接return。

RUNNING(还在运行,不能停)
TIDYING或TERMINATED(已经没有在运行的worker)
SHUTDOWN并且等待队列非空(执行完才能停)
标记2在worker非空的情况下又调用了interruptIdleWorkers,你可能疑惑在shutdown时已经调用过了,为什么又调用,而且每次只中断一个空闲worker?你需要知道,shutdown时worker可能在执行中,执行完阻塞在队列的take,不知道要结束,所有要补充调用interruptIdleWorkers。每次只中断一个是因为processWorkerExit时,还会执行tryTerminate,自动中断下一个空闲的worker。

标记3是最终的状态切换。线程池会先进入TIDYING状态,再进入TERMINATED状态,中间提供了terminated这个空方法供子类实现。

调用关闭线程池方法后,需要等待线程池切换到TERMINATED状态。awaitTermination检查限定时间内线程池是否进入TERMINATED状态,代码如下:

public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}

后言

以上过了一遍线程池主要的逻辑,总体来看线程池的设计是很清晰的。如有错误或不足,欢迎指出,也欢迎留言交流。

Tags:责任编辑:wangtianlin
顶一下(7)
100%
    1. 乐题库乐题库

      新云软件园乐题库下载专区为大家带来了乐题库一系列学习备考软件下载,其中包括乐题库app,乐题库电脑版,乐题库考研政治,乐题库司法考试等等!欢迎体验!...

    1. 万题库官网版万题库

      新云软件园万题库官网下载专区为大家提供了会计从业万题库,二建万题库,一建万题库,会计职称万题库等一系列的软件,欢迎大家下载使用!

    1. 猿题库猿题库

      新云手机站猿题库下载专区提供了猿题库官方最全面最新的猿题库app下载以及猿题库各类初高中解题app神器免费下载。

    文章评论 新云下载QQ群:① 7551827使用手机微信搜索 微信号:xinyunshouyou 关注我们 有更多惊喜!!

    请自觉遵守互联网相关政策法规,评论内容只代表网友观点,与本站立场无关!
      验证码:     登录   注册
    网友评论

    关注新云安卓游戏网

    卡司时时彩 www.541547.com-体彩门店编号| www.662335.com-新彩网双色球字迷| www.757040.com-75彩票网-| www.662708.com-体彩三亿在肥城| www.770785.com-体彩兑奖地点在哪里| www.854667.com-时时彩开彩计划| www.924600.com-秒速快三怎么稳赢| 购彩网www.78gcw.com| www.02vd.com-彩的五行属性是什么| www.xr50.com-身无分文买彩票| www.459758.com-体彩排列3d断组| www.040206.com-快三2同-| www.075970.com-菜鸟彩票-| www.384486.com-xy77幸运彩票| www.625531.com-专业购彩首页| www.753046.com-中彩吧平台可靠吗| www.857669.com-彩之源怎么看邀请码| www.978299.com-河南彩色路面材料| www.tw7.com-爱彩乐大发快三套路| www.2442.vip-最新版87彩店| www.655841.com-小财神彩票论坛分析| www.803795.com-福彩3d公式杀码| www.948374.com-福彩公益报谜语| 500万彩票www.39500a.com| www.uv81.com-湖北快三牛采走视图| www.6990.shop-吉林市新快三| www.438199.com-k彩登入-| www.302311.com-江苏快三共有多少注| www.ll19.com-彩票巴士官网下载| www.155770.com-微彩是不是骗局| www.616586.com-童神彩网高手榜| www.933582.com-七星彩开奖号码今天| 熊猫彩票www.333xm.cc| www.94554.com-足彩赔率大师破解版| www.947105.com-百度彩票开奖信息| 三星娱乐www.sxyl3.com| www.3417.org-存1元领彩金| www.299354.com-彩票每天赚两三千| www.389130.com-安徽彩票11选5| www.119403.com-江苏快三兑奖范围| www.760206.com-福彩彩票机保障| www.918103.com-u9彩票网站-| www.a11.vip-体彩大乐透兑奖表图| www.52705.com-七彩阳光图片| www.94dq.com-现在还有基诺彩票吗| www.097768.com-福彩内部人员卖号| www.9ja.com-四个数的有什么彩票| www.l42.net-彩81彩票官网| www.09917.com-乐彩网双色球| www.791990.com-附近体育彩票站| www.328519.com-双彩论坛彩吧网| www.465910.com-新浪足彩电脑版| www.709158.com-1305彩票合法吗| www.805780.com-彩票管理条例修改| www.887368.com-天下特彩吧网| www.964396.com-快三投注-| www.cai8966.com彩票的谜语-| www.b49.net-赢发彩票下载| www.29sx.com-彩票双色球专家选号| www.148.net-临沂彩虹谷值得玩吗| www.4213.biz-七彩格子app下载| www.01266.com-下载送彩金棋牌娱乐| www.323363.com-大庄家彩票网站| www.416166.com-天荣国际区块链彩票| www.48456.com-青海快三的号码推荐| www.97644.com-c38彩票官方网站| www.160140.com-竞彩胜平负不开售| www.835453.com-彩500网-| www.395789.com-当天彩票开奖| www.488300.com-足彩稳赚不赔攻略| www.675297.cc-百福利彩票开奖结果| www.069922.com-彩神计划下载| www.027018.com-八号彩票软件| www.1419.me-酷彩吧是否靠谱| www.355855.cc-微信分分彩哪里来的| www.590768.com-全国彩礼地图| www.878614.com-彩福快3-| www.975897.com-3d福彩杀码-| www.za63.com-体彩5d开奖走势图| www.028932.com-家彩网3d胆码预测| www.168270.com-快三跨度技巧规律| www.254072.com-华宇app彩票| www.834115.com-日景彩烟多少钱一个| www.982431.com-快三怎么买才能挣钱| www.pr88.com-快三走势图今天上海| www.16nt.com-重庆足彩分析师招聘| www.11521.cc-彩票中心客服电话| www.74440.cc-七乐彩奖池金额查询| www.025523.com-大发购彩是真的吗| www.991840.com-七乐彩最高奖金多少| www.hs69.com-竞彩足球app苹果| 云博彩票www.990733.com| www.212780.com-彩票黑平台-| www.311315.com-江苏体彩走势图| www.583120.com-福彩走势图技巧| www.46hx.com-萍乡竞彩网点| www.523182.com-破解彩票算法| www.62868.cc-彩票专业预测网站| www.c11.cc-爱彩乐是正规彩站吗| www.6448.me-彩票58-| www.40842.com-中国体彩网网| www.66744.cc-付进彩票站有多远| www.032960.com-乐彩神彩票靠谱吗| www.96522.com-竞猜型彩票怎么注册| www.116647.com-福彩3d最准规律| www.321265.com-南国彩票七星坛区| www.436191.com-大乐透还是七星彩| www.633842.com-东方亮彩被收购| www.804396.com-山西彩票网上买| www.905965.com-浙江之间福彩网| www.985282.com-728国际彩票官网| www.05nt.com-92彩票网-| www.7hi.cc-为什么彩票老是中奖| www.hp99.com-手机新浪彩票| www.277565.com-90彩票首页-| www.417679.com-体彩六加一开奖结果| www.776547.com-唐龙说彩组三点评| www.002305.com-时时彩四定规律| www.24ce.com-足彩总进球数怎么说| www.937206.com-10彩票app-| www.vb2.com-乐彩518-| www.813199.com-意大利彩金-| www.413193.com-篮球体彩奖金| 爱彩www.005ac.com| www.979743.com-玩赚彩票软件可信吗| www.96607.com-福彩胆是什么意思| www.06fp.com-3g彩票人工计划| www.87ve.com-做个黑彩平台多少钱| www.fr49.com-快三遗漏技巧| www.3050.bid-金祥彩票网官方网站| www.9340.biz-福利彩票大案| www.673176.com-福彩投注站号查询| www.190848.com-湖北福彩快3玩法| 博友彩www.755216.com| www.020830.com-福彩2d多少钱| www.md95.com-21彩票app-| www.620517.com-彩票稳赚方法| www.2053.cn-吴昕儿童画彩虹| www.410488.com-彩虹球-| www.32521.cc-中国最大的彩票论坛| www.3719.wang-3d字謎牛彩網| www.585882.com-福彩快三单双大小| www.826219.com-14号彩票-| www.82244.com-易彩登陆福地| www.5ow.cc-安徽快三出来多久了| www.78bm.com-境外可靠足彩| www.510247.com-大发快3是私家彩| www.611159.com-彩38领导者-| www.2134.biz-牛彩网图汇总九| www.976666.com-河南信阳娶媳妇彩礼| www.180198.com-彩票王牌app| www.761490.com-搜神传好彩妹和天尊| www.f56.site-马来西亚做网络博彩| www.2448.in-怎么看江苏快三走势| www.10947.com-澳客彩票官网| www.5414.com-七星彩票概率公式| www.279553.com-彩票龙虎规律| www.146644.com-易网彩票软件下载| www.234069.com-江苏最大的快三平台| www.328905.com-电子游艺绑定送彩金| www.386056.com-兼职帮别人投注彩票| www.504180.com-系统彩票是什么意思| www.835350.com-彩八彩票怎么下| www.288.blue-彩世界平台黑钱吗|