Future cancel迷惑性boolean入?yún)⒔馕?/h1>
更新時間:2023年02月28日 16:49:13 作者:Code皮皮蝦
這篇文章主要為大家介紹了Future cancel迷惑性boolean入?yún)⒔馕?,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
前言
當我們使用線程池submit
一個任務后,會返回一個Future
,而在Future
接口中存在一個cancel
方法,來幫助我們?nèi)∠羧蝿铡?/p>
但是cancel
方法有一個boolean
類型的入?yún)?,比較迷惑,之前也了解過該入?yún)?code>true 和 false的區(qū)別,但過一段時間之后就又忘了,遂寫了本文進行記錄,順便了解下源碼~
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning);
上面是cancel
方法的接口定義,當然英文看著麻煩,咱直接翻譯成看得懂的~
cancel
方法,會嘗試取消任務的執(zhí)行,但如果任務已經(jīng)完成、已經(jīng)取消或其他原因無法取消,則嘗試取消任務失敗。
如果取消成功,并且在取消時
- 該任務還未執(zhí)行,那么這個任務永遠不會執(zhí)行。
- 如果該任務已經(jīng)啟動,那么會根據(jù)
cancel
的boolean
入?yún)頉Q定是否中斷執(zhí)行此任務的線程來停止任務。
通過注釋我們大致能了解到cancel
的一個作用,但是還不夠細致,接下來我們通過源碼解讀詳細的帶大家了解一下~
FutureTask任務狀態(tài)認知
首先,我們先了解下FutureTask
中對任務狀態(tài)的定義
在使用線程池submit
后,實際上是返回的一個FutureTask
,而FutureTask
中對于任務定義了以下狀態(tài),并且在注釋中,也定義了狀態(tài)的流轉(zhuǎn)過程~
/**
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
但是通過對上面狀態(tài)定義的了解,我們可以發(fā)現(xiàn),在FutureTask
中并沒有一個表明任務處于執(zhí)行中的一個狀態(tài)!
直接看FutureTask
的run
方法源碼
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 執(zhí)行任務
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 執(zhí)行異常
setException(ex);
}
if (ran)
// 正常執(zhí)行完畢
set(result);
}
} finally {
//... 省略
}
}
?
protected void setException(Throwable t) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = t;
STATE.setRelease(this, EXCEPTIONAL); // final state
finishCompletion();
}
}
?
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
通過上面源碼,我們也能了解到
- 當任務正常執(zhí)行完畢時,任務狀態(tài)流轉(zhuǎn):
NEW -> COMPLETING -> NORMAL
- 任務執(zhí)行異常時,任務狀態(tài)流轉(zhuǎn):
NEW -> COMPLETING -> EXCEPTIONAL
所以,當任務剛創(chuàng)建,或者是任務在執(zhí)行過程中,任務的狀態(tài)都是NEW
cancel源碼分析
此時再來分析cancel
源碼
public boolean cancel(boolean mayInterruptIfRunning) {
// NEW為新建或者運行態(tài)
// 1. 此時任務已經(jīng)不是NEW,說明要么是完成要么是異常,取消不了,所以返回false
// 2. 此時任務還是NEW,如果我們傳入true,則CAS標記任務為INTERRUPTING,否則是CANCELLED
// 防止并發(fā)取消任務,CAS只會有一個線程成功,其余線程失敗
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 傳入true,則打斷該任務的執(zhí)行線程
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally {
// 比較任務狀態(tài)為INTERRUPTED
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
通過對FutureTask
任務狀態(tài)的認知,再結(jié)合對cancel
源碼的分析
我們可以總結(jié)出以下結(jié)論
當任務已經(jīng)完成或者異常時,無法取消任務
任務處于新建或者運行狀態(tài)時
cancel
方法入?yún)魅?code>true
將任務狀態(tài)NEW
-> INTERRUPTING
-> INTERRUPTED
,并打斷執(zhí)行該任務的線程
cancel
方法入?yún)魅?code>false
將任務狀態(tài)NEW
-> CANCELLED
但有個問題,傳入false
只是將狀態(tài)從NEW
變成CANCELLED
嘛,這好像沒啥用啊?
當然不是,此時我們需要再回頭看看FutureTask
的run
方法
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 執(zhí)行異常
setException(ex);
}
if (ran)
// 正常執(zhí)行完畢
set(result);
}
} finally {
//... 省略
}
}
run
方法開頭我們可以看到,如果任務的狀態(tài)不是NEW
,那么會直接return
,不執(zhí)行任務
那此時再想想傳入false
將任務狀態(tài)從NEW
-> CANCELLED
,是不是當任務還沒有開始執(zhí)行時,我們cancel(false)
就可以取消掉未執(zhí)行的任務了~
總結(jié)
通過上面的源碼解讀,我們大致能了解了cancel
的機制,但是我們還是完善的總結(jié)一下
任務如果不是NEW
狀態(tài)是不會執(zhí)行的
cancel
取消任務會改變?nèi)蝿盏臓顟B(tài)
- 如果傳入
true
, 則將任務狀態(tài)NEW
-> INTERRUPTING
-> INTERRUPTED
,并打斷執(zhí)行該任務的線程 - 如果傳入
false
,將任務狀態(tài)NEW
-> CANCELLED
傳入false
只能取消還未執(zhí)行的任務
傳入true
,能取消未執(zhí)行的任務,能打斷正在執(zhí)行的任務
擴展知識點
在cancel
源碼中,我們可以看到finally
中會去調(diào)用finishCompletion
那么,finishCompletion
是干啥的呢?
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
// 原子性將WAITERS設置為null
if (WAITERS.weakCompareAndSet(this, q, null)) {
// 遍歷WAITERS,將阻塞的線程都喚醒
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null;
q = next;
}
break;
}
}
?
// 擴展方法,交給自己實現(xiàn)
done();
?
callable = null;
}
大家可以想想,當我們submit
一個任務時,一般情況下都會需要去獲取他的返回值,會調(diào)用get
方法進行阻塞獲取
在FutureTask
中,會維護一條鏈表,該鏈表記錄了等待獲取該任務返回值被阻塞的線程
在調(diào)用get
方法時,會將組裝waiters
鏈表

所以,當我們?nèi)∠粋€任務時,是不是也應該去將阻塞等待獲取該任務的所有線程進行喚醒,而finishCompletion
方法就是做這個事情的~
以上就是Future cancel迷惑性boolean入?yún)⒔馕龅脑敿殐?nèi)容,更多關(guān)于Future cancel boolean入?yún)⒌馁Y料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
-
Spring Framework遠程代碼執(zhí)行漏洞分析(最新漏洞)
Spring Framework 是一個開源應用框架,旨在降低應用程序開發(fā)的復雜度,它具有分層體系結(jié)構(gòu),允許用戶選擇組件,同時還為 J2EE 應用程序開發(fā)提供了一個有凝聚力的框架,對Spring遠程代碼執(zhí)行漏洞相關(guān)知識感興趣的朋友一起看看吧 2022-04-04
-
Java使用Arrays.sort()方法實現(xiàn)給對象排序
這篇文章主要介紹了Java使用Arrays.sort()方法實現(xiàn)給對象排序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教 2021-12-12
-
使用Mybatis的Batch?Insert?Support?實現(xiàn)批量插入
這篇文章主要介紹了使用Mybatis的Batch?Insert?Support?實現(xiàn)批量插入。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教 2022-07-07
-
SpringBoot + Shiro前后端分離權(quán)限
這篇文章主要為大家詳細介紹了SpringBoot + Shiro前后端分離權(quán)限,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下 2019-12-12
最新評論
前言
當我們使用線程池submit
一個任務后,會返回一個Future
,而在Future
接口中存在一個cancel
方法,來幫助我們?nèi)∠羧蝿铡?/p>
但是cancel
方法有一個boolean
類型的入?yún)?,比較迷惑,之前也了解過該入?yún)?code>true 和 false的區(qū)別,但過一段時間之后就又忘了,遂寫了本文進行記錄,順便了解下源碼~
/** * Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, has already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when {@code cancel} is called, * this task should never run. If the task has already started, * then the {@code mayInterruptIfRunning} parameter determines * whether the thread executing this task should be interrupted in * an attempt to stop the task. * * <p>After this method returns, subsequent calls to {@link #isDone} will * always return {@code true}. Subsequent calls to {@link #isCancelled} * will always return {@code true} if this method returned {@code true}. * * @param mayInterruptIfRunning {@code true} if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete * @return {@code false} if the task could not be cancelled, * typically because it has already completed normally; * {@code true} otherwise */ boolean cancel(boolean mayInterruptIfRunning);
上面是cancel
方法的接口定義,當然英文看著麻煩,咱直接翻譯成看得懂的~
cancel
方法,會嘗試取消任務的執(zhí)行,但如果任務已經(jīng)完成、已經(jīng)取消或其他原因無法取消,則嘗試取消任務失敗。
如果取消成功,并且在取消時
- 該任務還未執(zhí)行,那么這個任務永遠不會執(zhí)行。
- 如果該任務已經(jīng)啟動,那么會根據(jù)
cancel
的boolean
入?yún)頉Q定是否中斷執(zhí)行此任務的線程來停止任務。
通過注釋我們大致能了解到cancel
的一個作用,但是還不夠細致,接下來我們通過源碼解讀詳細的帶大家了解一下~
FutureTask任務狀態(tài)認知
首先,我們先了解下FutureTask
中對任務狀態(tài)的定義
在使用線程池submit
后,實際上是返回的一個FutureTask
,而FutureTask
中對于任務定義了以下狀態(tài),并且在注釋中,也定義了狀態(tài)的流轉(zhuǎn)過程~
/** * Possible state transitions: * NEW -> COMPLETING -> NORMAL * NEW -> COMPLETING -> EXCEPTIONAL * NEW -> CANCELLED * NEW -> INTERRUPTING -> INTERRUPTED */ private volatile int state; private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; private static final int INTERRUPTING = 5; private static final int INTERRUPTED = 6;
但是通過對上面狀態(tài)定義的了解,我們可以發(fā)現(xiàn),在FutureTask
中并沒有一個表明任務處于執(zhí)行中的一個狀態(tài)!
直接看FutureTask
的run
方法源碼
public void run() { if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { // 執(zhí)行任務 result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; // 執(zhí)行異常 setException(ex); } if (ran) // 正常執(zhí)行完畢 set(result); } } finally { //... 省略 } } ? protected void setException(Throwable t) { if (STATE.compareAndSet(this, NEW, COMPLETING)) { outcome = t; STATE.setRelease(this, EXCEPTIONAL); // final state finishCompletion(); } } ? protected void set(V v) { if (STATE.compareAndSet(this, NEW, COMPLETING)) { outcome = v; STATE.setRelease(this, NORMAL); // final state finishCompletion(); } }
通過上面源碼,我們也能了解到
- 當任務正常執(zhí)行完畢時,任務狀態(tài)流轉(zhuǎn):
NEW -> COMPLETING -> NORMAL
- 任務執(zhí)行異常時,任務狀態(tài)流轉(zhuǎn):
NEW -> COMPLETING -> EXCEPTIONAL
所以,當任務剛創(chuàng)建,或者是任務在執(zhí)行過程中,任務的狀態(tài)都是NEW
cancel源碼分析
此時再來分析cancel
源碼
public boolean cancel(boolean mayInterruptIfRunning) { // NEW為新建或者運行態(tài) // 1. 此時任務已經(jīng)不是NEW,說明要么是完成要么是異常,取消不了,所以返回false // 2. 此時任務還是NEW,如果我們傳入true,則CAS標記任務為INTERRUPTING,否則是CANCELLED // 防止并發(fā)取消任務,CAS只會有一個線程成功,其余線程失敗 if (!(state == NEW && STATE.compareAndSet (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // 傳入true,則打斷該任務的執(zhí)行線程 if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) t.interrupt(); } finally { // 比較任務狀態(tài)為INTERRUPTED STATE.setRelease(this, INTERRUPTED); } } } finally { finishCompletion(); } return true; }
通過對FutureTask
任務狀態(tài)的認知,再結(jié)合對cancel
源碼的分析
我們可以總結(jié)出以下結(jié)論
當任務已經(jīng)完成或者異常時,無法取消任務
任務處于新建或者運行狀態(tài)時
cancel
方法入?yún)魅?code>true
將任務狀態(tài)NEW
-> INTERRUPTING
-> INTERRUPTED
,并打斷執(zhí)行該任務的線程
cancel
方法入?yún)魅?code>false
將任務狀態(tài)NEW
-> CANCELLED
但有個問題,傳入false
只是將狀態(tài)從NEW
變成CANCELLED
嘛,這好像沒啥用啊?
當然不是,此時我們需要再回頭看看FutureTask
的run
方法
public void run() { if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; // 執(zhí)行異常 setException(ex); } if (ran) // 正常執(zhí)行完畢 set(result); } } finally { //... 省略 } }
run
方法開頭我們可以看到,如果任務的狀態(tài)不是NEW
,那么會直接return
,不執(zhí)行任務
那此時再想想傳入false
將任務狀態(tài)從NEW
-> CANCELLED
,是不是當任務還沒有開始執(zhí)行時,我們cancel(false)
就可以取消掉未執(zhí)行的任務了~
總結(jié)
通過上面的源碼解讀,我們大致能了解了cancel
的機制,但是我們還是完善的總結(jié)一下
任務如果不是NEW
狀態(tài)是不會執(zhí)行的
cancel
取消任務會改變?nèi)蝿盏臓顟B(tài)
- 如果傳入
true
, 則將任務狀態(tài)NEW
->INTERRUPTING
->INTERRUPTED
,并打斷執(zhí)行該任務的線程 - 如果傳入
false
,將任務狀態(tài)NEW
->CANCELLED
傳入false
只能取消還未執(zhí)行的任務
傳入true
,能取消未執(zhí)行的任務,能打斷正在執(zhí)行的任務
擴展知識點
在cancel
源碼中,我們可以看到finally
中會去調(diào)用finishCompletion
那么,finishCompletion
是干啥的呢?
private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { // 原子性將WAITERS設置為null if (WAITERS.weakCompareAndSet(this, q, null)) { // 遍歷WAITERS,將阻塞的線程都喚醒 for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; q = next; } break; } } ? // 擴展方法,交給自己實現(xiàn) done(); ? callable = null; }
大家可以想想,當我們submit
一個任務時,一般情況下都會需要去獲取他的返回值,會調(diào)用get
方法進行阻塞獲取
在FutureTask
中,會維護一條鏈表,該鏈表記錄了等待獲取該任務返回值被阻塞的線程
在調(diào)用get
方法時,會將組裝waiters
鏈表
所以,當我們?nèi)∠粋€任務時,是不是也應該去將阻塞等待獲取該任務的所有線程進行喚醒,而finishCompletion
方法就是做這個事情的~
以上就是Future cancel迷惑性boolean入?yún)⒔馕龅脑敿殐?nèi)容,更多關(guān)于Future cancel boolean入?yún)⒌馁Y料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Framework遠程代碼執(zhí)行漏洞分析(最新漏洞)
Spring Framework 是一個開源應用框架,旨在降低應用程序開發(fā)的復雜度,它具有分層體系結(jié)構(gòu),允許用戶選擇組件,同時還為 J2EE 應用程序開發(fā)提供了一個有凝聚力的框架,對Spring遠程代碼執(zhí)行漏洞相關(guān)知識感興趣的朋友一起看看吧2022-04-04Java使用Arrays.sort()方法實現(xiàn)給對象排序
這篇文章主要介紹了Java使用Arrays.sort()方法實現(xiàn)給對象排序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12使用Mybatis的Batch?Insert?Support?實現(xiàn)批量插入
這篇文章主要介紹了使用Mybatis的Batch?Insert?Support?實現(xiàn)批量插入。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07SpringBoot + Shiro前后端分離權(quán)限
這篇文章主要為大家詳細介紹了SpringBoot + Shiro前后端分離權(quán)限,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12