背景(註釋):
使用Callable可以定義一個返回結果的任務(過程中可能拋出異常)。
實現它只需要返回一個結果,不提供參數。
這個Callabel接口類似於Runnable,兩者都是被設計用於被其他線程執行。區別是Runnable無法返回結果以及不能拋出受檢查異常。
Executors中包含一共公共方法用於轉化其他的實例爲Callable實例。
public interface Callable<V> {
V call() throws Exception;
}
通過Future可以得到異步計算的結果。這個接口支持檢查任務是否執行完成,等待任務完成,以及獲取計算完成之後的結果。計算結果只能通過get方法來獲取,當任務未完成時會阻塞。通過cancel方法實現取消機制。附加的方法被提供能夠用於確定任務已經正式完成或者被取消。當一個計算任務完成,任務的狀態就不能變爲取消。如果你使用Future的目的僅僅只是爲了得到取消的能力,那麼你可以聲明Future<?>以及通過返回null作爲一個結果。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
使用場景:
Future通過與ExecutorService、Callable配合使用,從而得到計算結果。
interface ArchiveSearcher { String search(String target); }
class App {
ExecutorService executor = ...
ArchiveSearcher searcher = ...
void showSearch(final String target)
throws InterruptedException {
Future<String> future
= executor.submit(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
displayOtherThings(); // do other things while searching
try {
displayText(future.get()); // use future
} catch (ExecutionException ex) { cleanup(); return; }
}
}
FutureTask實現了Future以及Runnable接口,所以能夠被Executor執行,比如上面的例子可以用如下的例子代替:
FutureTask<String> future =
new FutureTask<String>(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
executor.execute(future);
下面我們再來介紹JUC中實現了Future/Runnable的一個類:FutureTask。
首先FutureTask,作爲一個可取消的異步計算任務。這個類提供了一個Future的基本實現,以及開始計算和取消計算的方法,查詢計算是否完成,以及索取計算結果的方法。結果僅在計算完成之後才能獲取。get方法將會在計算結果還沒時阻塞。計算完成之後,這個計算就不能重新開啓或者取消了(除非通過runAndRest方法)。
FutureTask能夠用於包裝Callable或者Runnable。因爲FutureTask實現了Runnable,一個FutureTask能夠被提交到Executor去執行(事實上也是這麼構造的)。這個類還提供了受保護的方法能夠用於定製自己的策略。
實現:
首先我們來看實際執行任務的run方法:
public void run() {
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 {
runner = null;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
詳情如下:
- 首先檢查state假如不爲NEW或者沒有設置當前線程爲執行線程,則任務已經被執行過或者被其他線程執行中,返回。
- 取得調用任務Callable,那麼設置執行任務,並且用ran記錄任務狀態,假如有異常則執行setException。
- 否則ran爲true,任務執行完成則調用set方法。
- 最後runnner置爲null,假如此刻狀態>=INTERRUPTING,則退讓等待狀態變爲INTERRUPTED。
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
可以看出來他們之間的區別僅在於EXCEPTIONAL和NORMAL,這裏EXCEPTIONAL爲異常狀態,NORMAL表明任務已經正式完成。
這個還有個COMPLETING中間狀態(這個是事實上的決定任務結果的CAS操作,它後面的put操作是在結果已定的情況下執行,所以不需要CAS,並且這一步的作用是happens-before,傳遞結果值outcome)。
我們再來看get操作:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
分別調用await的限時和非限時版本,最後返回的值<=COMPLETING則爲超時狀態。
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;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
詳情如下:
- 與所有其他的阻塞策略相比,這裏較簡單,探測的條件爲s>COMPLETING。
- 首先探測線程是否中斷,如果中斷則嘗試從當前棧中刪除這個等待者,並拋出異常InterruptedException。
- 在狀態爲COMPLETING時採取退讓策略yield。
- 否則構造wait節點,進入LIFO隊列,然後再限時或者非限時阻塞。
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
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; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
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;
}
mayInterruptIfRunning用於指示是否強制取消。- 首先判斷當前狀態state,假如已經改變或者CAS操作在此失敗,那麼久返回false說明取消失敗(原因是任務執行完成或者任務異常)。
- 成功之後假如參數爲true,則試着中斷當前的執行任務,並且將狀態最終改變爲INTERRUPTED。
- 最後同樣調用finishCompletion並返回true。