一、簡介
FutureTask 可取消的異步計算(主線程可以去做其他的)。 此類提供Future的基本實現,其中包含啓動和取消計算,查詢以查看計算是否完成以及獲取計算結果的方法。 只有在計算完成後才能獲取結果; 如果計算尚未完成,則get方法將阻塞。 一旦計算完成,就不能重新啓動或取消計算(除非使用runAndReset調用計算)。
FutureTask可用於包裝Callable或Runnable對象。 由於FutureTask實現Runnable,因此FutureTask可以提交給執行程序以執行。
除了用作獨立類之外,此類還提供受保護的功能,這些功能在創建自定義任務類時可能會有用。
二、源碼閱讀 (基於 Java8)
Java 併發包核心要素, 狀態位stat,隊列,CAS,Java8的實現依然是這樣實現。
修訂說明:與本版本的先前版本 Java7 不同
依賴AbstractQueuedSynchronizer的類,主要用於
避免使用戶驚訝於在取消競爭的時候保留中斷狀態
。 當前設計中的同步控制依賴
通過CAS更新“狀態”字段以跟蹤完成情況,以及
一個簡單的Treiber堆棧來保存等待線程。
樣式說明:與往常一樣,我們繞過了使用的開銷
AtomicXFieldUpdaters,而是直接使用Unsafe內部函數。
FutureTask 實現了 Runnable 和 Future。
先看下靜態代碼塊,CAS操作主要針對3個屬性,包括state、runner和waiters,說明這3個屬性基本是會被多個線程同時訪問的。其中state屬性代表了任務的狀態,waiters屬性代表了指向棧頂節點的指針,runner屬性代表了執行FutureTask中的“Task”的線程:
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long stateOffset;
private static final long runnerOffset;
private static final long waitersOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = FutureTask.class;
stateOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("state"));
runnerOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));
waitersOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("waiters"));
} catch (Exception e) {
throw new Error(e);
}
}
此任務的運行狀態,最初爲NEW。 運行狀態僅在set,setException和cancel方法中轉換爲終止狀態。 在完成期間,狀態可能會採用COMPLETING(正在設置結果時)或INTERRUPTING(僅在中斷正在跑的線程滿足cancel(true)時)的瞬態值。 從這些中間狀態到最終狀態的轉換使用開銷低的有序/惰性寫入,因爲值是唯一的,無法進一步修改。 可能的狀態轉換:
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;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** get()返回的結果對象 or exception to throw from */
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 */
private volatile WaitNode waiters;
// 構造函數 ,狀態初始化爲 NEW
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
// V 是返回的結果類型 。 用一個包裝類實現 Callable
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
既然實現了 Runnable 肯定有run方法來給線程執行,先看下run方法:
public void run() {
// 就檢查當前狀態是不是New, 如果不是則使用CAS操作將runner屬性設置位當前線程
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
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;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// 在finally塊中,我們將runner屬性置爲null,並且檢查有沒有遺漏的中斷,如果發現s >= INTERRUPTING, 說明執行任務的線程有可能被中斷了,因爲s >= INTERRUPTING 只有兩種可能,state狀態爲INTERRUPTING和INTERRUPTED。
有的同學可能就要問了,咱前面已經執行過的set方法或者setException方法不是已經將state狀態設置成NORMAL或者EXCEPTIONAL了嗎?怎麼會出現INTERRUPTING或者INTERRUPTED狀態呢?別忘了,咱們在多線程的環境中,在當前線程執行run方法的同時,有可能其他線程取消了任務的執行,此時其他線程就可能對state狀態進行改寫,這也就是我們在設置終止狀態的時候用putOrderedInt方法,而沒有用CAS操作的原因——我們無法確信在設置state前是處於COMPLETING中間態還是INTERRUPTING中間態。
// 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);
}
}
//除非已經設置或取消了該Future,否則將此Future的結果設置爲給定值。
成功完成計算後,run方法會在內部調用此方法。 狀態轉換 NEW->COMPLETING->NORMAL
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//
finishCompletion();
}
}
// 等待棧中移除節點,通知所有等待線程,調用 down(),並把 callable = null
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) { // 等待棧不爲空
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { // 將waitersOffset從q賦值爲null
for (;;) { // 死循環釋放所有等待線程,線程有些線程在 run這個方法,有些可能想取消
Thread t = q.thread;
if (t != null) { // 釋放當前執行完的節點
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done(); // FutureTask 是空方法
callable = null; // to reduce footprint
}
看下獲取結果的代碼get():
// FutureTask中會涉及到兩類線程,一類是執行任務的線程,它只有一個,FutureTask的run方法就由該線程來執行;一類是獲取任務執行結果的線程,它可以有多個,這些線程可以併發執行,每一個線程都是獨立的,都可以調用get方法來獲取任務的執行結果。如果任務還沒有執行完,則這些線程就需要進入Treiber棧中掛起,直到任務執行結束,或者等待的線程自身被中斷。
public V get() throws InterruptedException, ExecutionException {
// 最初狀態爲 NEW = 0 COMPLETING = 1
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
// 根據當前state狀態,返回的結果,或拋出異常
return report(s);
}
// 等待結果執行完成,或者終止或者中斷或者超時
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) { // 死循環等待一個結果
if (Thread.interrupted()) { // 線程中斷則等待池中移除節點,然後拋出異常
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) { // 線程運行結束
if (q != null)
q.thread = null; // 方便GC
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield(); // 線程執行完成,給調度程序的提示是當前線程願意放棄對處理器的當前使用。調度程序可以隨意忽略此提示。
else if (q == null) // 第一次執行則先創建等待節點,結構是 線程和next節點。 和AQS 隊列類似
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q); // CAS操作將新建的q節點添加到waiters鏈表的頭節點之前,其實就是Treiber棧的入棧操作
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) { // 超時則移出等待隊列,返回結果
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
任務取消執行:
public boolean cancel(boolean mayInterruptIfRunning) { // 根據狀態轉換,只有state爲NEW 才能取消
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;
}
總結
FutureTask 可以讓主線程去執行其他操作,但是 get 結果是同步等待結果計算完成,想異步獲取執行結果,可以看下博主的 這篇文章 Java 源碼解析 ---- ExecutorCompletionService 進一步瞭解
參考資料
JDK 8 源碼