前言
線程并發(fā)系列文章:
Java 線程基礎(chǔ)
Java 線程狀態(tài)
Java “優(yōu)雅”地中斷線程-實(shí)踐篇
Java “優(yōu)雅”地中斷線程-原理篇
真正理解Java Volatile的妙用
Java ThreadLocal你之前了解的可能有誤
Java Unsafe/CAS/LockSupport 應(yīng)用與原理
Java 并發(fā)"鎖"的本質(zhì)(一步步實(shí)現(xiàn)鎖)
Java Synchronized實(shí)現(xiàn)互斥之應(yīng)用與源碼初探
Java 對(duì)象頭分析與使用(Synchronized相關(guān))
Java Synchronized 偏向鎖/輕量級(jí)鎖/重量級(jí)鎖的演變過(guò)程
Java Synchronized 重量級(jí)鎖原理深入剖析上(互斥篇)
Java Synchronized 重量級(jí)鎖原理深入剖析下(同步篇)
Java并發(fā)之 AQS 深入解析(上)
Java并發(fā)之 AQS 深入解析(下)
Java Thread.sleep/Thread.join/Thread.yield/Object.wait/Condition.await 詳解
Java 并發(fā)之 ReentrantLock 深入分析(與Synchronized區(qū)別)
Java 并發(fā)之 ReentrantReadWriteLock 深入分析
Java Semaphore/CountDownLatch/CyclicBarrier 深入解析(原理篇)
Java Semaphore/CountDownLatch/CyclicBarrier 深入解析(應(yīng)用篇)
最詳細(xì)的圖文解析Java各種鎖(終極篇)
線程池必懂系列
在Android開發(fā)中,不可避免的會(huì)用到線程來(lái)執(zhí)行耗時(shí)任務(wù)苫幢,那如果我們想在中途停止/中斷任務(wù)的執(zhí)行究飞,該怎么辦呢屡谐?先來(lái)看看一個(gè)簡(jiǎn)單的線程。
private Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
Log.d(TAG, "thread isAlive:" + threadOne.isAlive());
Thread.sleep(1000);
}
} catch (Exception e) {
Log.d(TAG, e.getClass().toString());
Log.d(TAG, "thread isAlive in catch:" + threadOne.isAlive());
}
}
});
正常運(yùn)行打印結(jié)果:
當(dāng)使用interrupt()方法中斷該線程時(shí)斯辰,打印如下:
可以看出八回,調(diào)用interrupt()后萄焦,會(huì)捕獲名為“InterruptedException”的異常,但是接下來(lái)的發(fā)現(xiàn)線程還存活凤瘦,這是怎么回事呢宿礁?既然線程能夠被中斷,那么是否提供查詢中斷狀態(tài)的方法呢蔬芥?通過(guò)查看api我們發(fā)現(xiàn)梆靖,thread.isInterrupted()可以查看線程的中斷狀態(tài),因此我們?cè)偌右粋€(gè)打颖仕小:
Log.d(TAG, e.getClass().toString());
Log.d(TAG, "thread isInterrupted::" + threadOne.isInterrupted());
Log.d(TAG, "thread isAlive in catch:" + threadOne.isAlive());
然而中斷狀態(tài)位依然是“未被中斷”返吻。這與我們想象的不太一樣赂鲤,因此回想一下是哪個(gè)方法拋出了異常藤为,發(fā)現(xiàn)是sleep方法。
// BEGIN Android-changed: Implement sleep() methods using a shared native implementation.
public static void sleep(long millis) throws InterruptedException {
sleep(millis, 0);
}
我們先把sleep()方法注釋掉望浩,再運(yùn)行
private Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
try {
while (!threadOne.isInterrupted()) {
Log.d(TAG, "thread isAlive:" + threadOne.isAlive());
}
Log.d(TAG, "break while thread isAlive:" + threadOne.isAlive());
Log.d(TAG, "break while thread isInterrupted::" + threadOne.isInterrupted());
} catch (Exception e) {
Log.d(TAG, e.getClass().toString());
Log.d(TAG, "thread isInterrupted::" + threadOne.isInterrupted());
Log.d(TAG, "thread isAlive in catch:" + threadOne.isAlive());
}
}
});
這次的結(jié)果比較符合我們的“直觀想象”, 線程還是存活次酌,但中斷狀態(tài)位標(biāo)記位為true恨课。
從上面兩個(gè)兩個(gè)例子可知:
1.中斷正在阻塞(sleep)的線程,會(huì)拋出InterruptedException異常岳服,中斷標(biāo)記位為false剂公。
2.中斷未被阻塞的線程,中斷標(biāo)記位會(huì)被置為true吊宋。
那么針對(duì)正在阻塞的線程纲辽,我們只需要捕獲到InterruptedException異常就退出線程執(zhí)行,對(duì)于未被阻塞的線程,判斷中斷標(biāo)記是否為true拖吼,若是則退出線程執(zhí)行鳞上。當(dāng)然我們線程里如果調(diào)用了其它方法,不確定其它方法阻塞與否吊档,因此可以將這兩種判斷結(jié)合起來(lái)篙议,如下:
private Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
try {
while (!threadOne.isInterrupted()) {
doSomething();
}
} catch (Exception e) {
if (e instanceof InterruptedException) {
isInterrupted = true;
}
}
}
});
也許你會(huì)疑惑說(shuō)你上面的線程都是在while里跑,如果線程只走一次怠硼,怎么中斷呢鬼贱?只能盡可能在每個(gè)關(guān)鍵之處停止其執(zhí)行。
private Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
try {
if (!threadOne.isInterrupted())
doSomething1();
else
return;
if (!threadOne.isInterrupted())
doSomething2();
else
return;
if (!threadOne.isInterrupted())
doSomething3();
else
return;
} catch (Exception e) {
}
}
});
除了sleep方法之外香璃,還有其它方法會(huì)拋出異常不这难?實(shí)際上,在Thread源碼里對(duì)此都有解釋葡秒,我們來(lái)看看源碼怎么說(shuō)的姻乓。
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
總結(jié)來(lái)說(shuō):
- 在線程里使用sleep、wait眯牧、join等方法蹋岩,當(dāng)線程被中斷時(shí),中斷狀態(tài)位會(huì)被重置為false炸站,并且拋出InterruptedException異常(這也是為什么我們第一個(gè)例子里thread.isInterrupted()為false的原因)
- 在線程里使用nio InterruptibleChannel接口時(shí)星澳,當(dāng)線程被中斷時(shí),中斷狀態(tài)位會(huì)被重置為true旱易,并且拋出ClosedByInterruptException異常
- 在線程里使用nio Selector時(shí)禁偎,當(dāng)線程被中斷時(shí),中斷狀態(tài)位會(huì)被重置為true
- 如不屬于上述條件阀坏,則中斷狀態(tài)位會(huì)被重置為true(對(duì)應(yīng)我們上面說(shuō)的沒(méi)有阻塞的情況)
thread.isInterrupted() 和 Thread.interrupted()區(qū)別
thread.isInterrupted()是對(duì)象方法如暖,表示thread的中斷狀態(tài)。Thread.interrupted()是靜態(tài)方法忌堂,表示當(dāng)前線程的中斷狀態(tài)盒至,舉個(gè)例子:
Log.d(TAG, " curThread is:" + Thread.currentThread().getName());
Log.d(TAG, " Thread.currentThread().isInterrupted() before :" + Thread.currentThread().isInterrupted());
Log.d(TAG, " Thread.interrupted() before :" + Thread.interrupted());
Log.d(TAG, " threadOne.isInterrupted() before :" + threadOne.isInterrupted());
Thread.currentThread().interrupt();
Log.d(TAG, " Thread.currentThread().isInterrupted() after:" + Thread.currentThread().isInterrupted());
Log.d(TAG, " Thread.interrupted() after :" + Thread.interrupted());
Log.d(TAG, " Thread.currentThread().isInterrupted() after2:" + Thread.currentThread().isInterrupted());
Log.d(TAG, " threadOne.isInterrupted() after :" + threadOne.isInterrupted());
從上面可以看出來(lái),Thread.interrupted()調(diào)用后會(huì)重置中斷狀態(tài)為false士修,而thread.isInterrupted()卻不會(huì)枷遂。
總結(jié)
1、線程正在執(zhí)行sleep棋嘲、join酒唉、wait等方法,此時(shí)線程處在WAITING/TIMED_WAITING狀態(tài)沸移,當(dāng)執(zhí)行thread.interrupt()痪伦,那么會(huì)拋出InterruptedException異常侄榴,線程中斷標(biāo)記位為false,線程停止運(yùn)行网沾;
2癞蚕、線程處在RUNNABLE狀態(tài),當(dāng)執(zhí)行thread.interrupt()辉哥,不會(huì)拋出異常桦山,線程中斷標(biāo)記位為true,線程未停止運(yùn)行证薇;
3度苔、如果線程處在BLOCKED(Synchronized爭(zhēng)搶鎖)狀態(tài),當(dāng)執(zhí)行thread.interrupt()浑度,不會(huì)拋出異常,線程中斷標(biāo)記位為true鸦概,線程未停止運(yùn)行(這點(diǎn)也說(shuō)明了Synchronized不可打斷)
更多關(guān)于線程狀態(tài)的問(wèn)題請(qǐng)移步:Java 線程狀態(tài)