目錄
1.1 FutureTask(Callable callable)
1.2 FutureTask(Runnable runnable, V result)
1.3.4 指向傳入的實現了Callable接口的對象索引 callable
1.3.6 一個帶向鏈表結構的線程鏈表中的一個節點(此部分博主還不太明白)
1.4.5 get(long timeout, TimeUnit unit)
1.5.2 awaitDone(boolean timed, long nanos)
1.5.3 removeWaiter(WaitNode node)
1.5.5 handlePossibleCancellationInterrupt(int s)
1.6.2 setException(Throwable t)
FutureTask類實現了RunnableFuture接口相關功能。RunnableFuture接口源碼解讀
此類的對象可以看成是一個可以對任務進行一些操作的實體,或者封裝。
FutureTask類中:
2個有參構造器。
6個public修飾的方法。
5個private修飾的私有方法。
4個protected修飾的方法
1個靜態內部類。
1個靜態代碼塊。
一.源碼解讀
先來看看此類的兩個有參的構造器
1.1 FutureTask(Callable<V> callable)
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
此構造器作用是傳入一個實現了Callable接口的的任務類,初始化該FutureTask能操作的任務類。
1.2 FutureTask(Runnable runnable, V result)
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
如上圖此構造器的作用是傳入一個實現了Runnable接口的任務類,並設置當此任務類運行完成後,任務類需要向調用此類的get方法的對象返回的 結果(result)。
此構造器會調用Executors類的靜態方法 callable(Runnable task, T result) 如下圖
/**
* Returns a {@link Callable} object that, when
* called, runs the given task and returns the given result. This
* can be useful when applying methods requiring a
* {@code Callable} to an otherwise resultless action.
* @param task the task to run
* @param result the result to return
* @param <T> the type of the result
* @return a callable object
* @throws NullPointerException if task null
*/
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
而此靜態方法會返回一個實現了Executors中的靜態內部類:RunnableAdapter。如下圖
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
如上圖可以看見,此類就是實現了Callable接口,使得實現Runnable的類也能獲取返回值,雖然此返回值是初始化任務時設置的。之前只實現Runnable接口的類,線程執行完這些類後是不會有返回值的。此RunnableAdapter<T>也是對適配器模式的實現.
以上模塊我們能知道,此構造器的作用就是讓只實現了Runnable接口的任務類,也能擁有我們設置的result返回值。類型是個泛型。因此返回的對象很寬泛,並不是說只能返回字符串等。
由於當前FutureTask類實現了RunnableFuture接口,當然需要實現它對應的功能,看完構造器,我們來看看他public修飾的方法。來看看這些方法可以對傳入的(實現了Callable或Runnable的)任務類做出那些操作。
以下對傳入的任務類統稱任務X(不論實現了什麼接口的任務類)
1.3 FutureTask類的一些常量和屬性
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* 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;
在進入第1.4之前我們需要先了解一下此類的一些屬性和字段。
上圖的屬性都被private修飾,意義它們是此類自己使用的屬性,不對外暴露。
1.3.1 任務當前狀態 state
首先第一個state,顧名思義,爲當前任務X的狀態。它被volatile修飾,意義是同時只能被一個線程操作他的狀態。
注意註釋中說明了只有這四種狀態的變化:
1 * NEW -> COMPLETING -> NORMAL
2 * NEW -> COMPLETING -> EXCEPTIONAL
3 * NEW -> CANCELLED
4 * NEW -> INTERRUPTING -> INTERRUPTED
1.3.2 任務7種狀態屬性:
NEW:代表此任務X正在從調用start()方法到任務正在運行的狀態。
COMPLETING:代表此任務X運行結束,返回結果的時候,此時結果還未裝載到outcome字段中(下面會提到)。完成中!
NORMAL:代表任務X執行成功,outcome字段保存正常返回值。
EXCEPTIONAL:代表任務X執行拋異常,outcome字段保存異常信息。
CANCELLED:任務X從start()方法開始到任務運行完成之前,用戶調用了cancel(false)方法。因爲cancel(false)會允許正在執行任務的線程執行完任務。所以任務最後會被執行完。所以從cancel(false)方法被調用到任務執行完畢。任務狀態都會被置爲CANCELLED。 請參考future接口中cancel的定義:Future接口(源碼解讀)
INTERRUPTING:任務X從start()方法開始到任務運行完成之前,用戶調用了cancel(true)方法。此方法會調用interrupt()方法把中斷標誌置爲true。當線程輪詢到此標誌位爲true時,會做相關操作,比如真正的中斷線程。例如說執行return或其他操作,拋出異常等。有關interrupt()方法可以參考博客:(先做個假鏈接,這裏我要是忘了寫,你們要麼提醒我一下,要麼百度一下)。
而在調用cancel(true)方法到任務被中斷之前,狀態標誌位爲INTERRUPTING
INTERRUPTED:當線程被真正中斷後,任務的狀態標誌爲此標誌。
1.3.3 存放返回結果 outcome:
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
1.3.4 指向傳入的實現了Callable接口的對象索引 callable
/** The underlying callable; nulled out after running */
private Callable<V> callable;
1.3.5 運行Callable的線程 runner
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
1.3.6 一個帶向鏈表結構的線程鏈表中的一個節點(此部分博主還不太明白)
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
1.4 6個Public修飾的方法
1.4.1 isCancelled()
當任務狀態大於等於CANCELLED時,返回true。
public boolean isCancelled() {
return state >= CANCELLED;
}
1.4.2 isDone()
當任務狀態只要不等於NEW時,返回true。由此可以說明,除了NEW的狀態,都可視爲任務已經運行完成了。至於完成成功與失敗不做考慮。
public boolean isDone() {
return state != NEW;
}
1.4.3 cancel()
實現了future接口的方法,此FutureTask類實現了RunnableFuture接口,而RunnableFuture接口又繼承了future接口。future接口中有cancel方法。
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;
}
首先我們來看一下這個方法的第一個if判斷:
當任務X當前狀態不爲NEW時,這裏是個&&,因此後面的compareAndSwapInt方法並不會執行。
cancel方法直接返回false。
(1)意味着任務X當前的狀態不爲NEW時,不能使用cancel方法,cancel方法不會執行任何操作並返回false。
當任務X當前的狀態爲NEW時,我們再來看看這個方法。
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)
這個方法是Unsafe類的(native)本地方法。也是對CAS思想的實現。關於CAS:
/**
* Atomically update Java variable to <tt>x</tt> if it is currently
* holding <tt>expected</tt>.
* @return <tt>true</tt> if successful
*/
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
此方法的意思是,當存放對象o中偏移量爲offset的值與期望值excepted相等時,把對象o中偏移量爲offset的值修改爲x。
至此我們返回上面的if判斷:
(2)意味着任務X當前的狀態爲NEW時,且傳入的mayInterruptIfRunning爲false時。任務的狀態被切換爲CANCELLED
且繼續運行try部分代碼,此時mayInterruptIfRunning爲false,所以直接執行finally代碼塊中的finishCompletion()方法:
這裏我們轉到1.5.9節。好了看完finishCompletion()方法我們知道了只要執行cancel方法,那麼waiters鏈表中所有節點的任務都會被取消。
(3)意味着任務X當前的狀態爲NEW時,且傳入的mayInterruptIfRunning爲true時。任務的狀態被切換爲INTERRUPTING。並且告知線程需要被中斷。執行了interrupt方法。這裏我們繼續來看看UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
public native void putOrderedInt(Object o, long offset, int x);
這個方法也是一個本地方法,意味着讓虛擬機去設置內存屏障,避免指令重排序。作用是把offset位置的值修改爲x。而且只有當offset位置的值被volatile修飾時,它被更新爲x後才能立即從工作內存刷新到主內存中,這裏大家應該能發現1.3.1中state就是被volatile修飾的。這裏我們需要了解JMM的知識。
當然在這裏就是把狀態更新爲INTERRUPTED
看完上面3點,我們走完了1.3.1中第3種和第4種狀態變化。
1.4.4 get()
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
這方法是實現父接口的父接口future中的get方法。調用此方法的線程會阻塞到得到任務返回的結果。
裏面用了awaitDone方法:請看1.6.1。
此方法意味着當任務未完成時纔會去調用awaitDone的方法,看完1.6.1我們也知道,此方法會在大於COMPLETING時返回任務狀態。其他狀態要麼yield要麼park了。當大於COMPLETING狀態時(可再次查看1.3.1查看狀態變化)不進入awaitDone方法直接返回狀態。因爲只要完成之後,狀態就更新了,可以直接返回狀態。
1.4.5 get(long timeout, TimeUnit unit)
/**
* @throws CancellationException {@inheritDoc}
*/
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);
}
此方法和get無參方法差不多,只是當任務未結束時,已經到達了等待時間,那麼會拋出超時異常。
1.4.6 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 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);
}
}
實現了接口的方法,此方法纔是核心運行任務的方法,看源碼,它首先判斷任務是否爲NEW,不爲NEW就直接返回了,從此可知,重複運行run方法是沒有用的,同一個任務只運行一次。
如果當前的執行Callable的線程runner爲null,那麼他會讓當前線程去成爲runner。執行Callable。這也是run的意義。當然如果不爲null,已經有runner去執行任務了,那麼也直接run方法也直接return。
try塊中的代碼就是執行Callable的call方法。並獲取結果result。不拋異常就調用set方法,那麼我們來看一下set方法:見1.7.1節
那麼當拋出異常我們需要調用setException()方法:
1.5 5個private修飾的方法
1.5.1 finishCompletion()
此方法是私有的供FutureTask類自己使用,它的作用是釋放waiters單鏈表中所有節點的線程和任務資源。
/**
* Removes and signals all waiting threads, invokes done(), and
* nulls out callable.
*/
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {//循環完所有waiters鏈表的節點,讓線程置空,並喚醒它。
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
}
在看這個方法的源碼之前我們先看一下靜態代碼塊部分,看完靜態代碼塊部分我們看for循環,這個for循環首先定義一個WaitNode q,讓他指向一個waiters節點,並且不能爲空(這也是循環條件),;後不寫任何自增的代碼。接下來獲取節點中的線程t,如果t不爲空,那麼讓它變爲空,接下來我們看看這個方法:LockSupport.unpark(t);
/**
* Makes available the permit for the given thread, if it
* was not already available. If the thread was blocked on
* {@code park} then it will unblock. Otherwise, its next call
* to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* thread has not been started.
*
* @param thread the thread to unpark, or {@code null}, in which case
* this operation has no effect
*/
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
這方法就是對判斷thread不爲空才繼續調用UNSAFE的unpark方法,我們再往下跟:
/**
* Unblock the given thread blocked on <tt>park</tt>, or, if it is
* not blocked, cause the subsequent call to <tt>park</tt> not to
* block. Note: this operation is "unsafe" solely because the
* caller must somehow ensure that the thread has not been
* destroyed. Nothing special is usually required to ensure this
* when called from Java (in which there will ordinarily be a live
* reference to the thread) but this is not nearly-automatically
* so when calling from native code.
* @param thread the thread to unpark.
*
*/
public native void unpark(Object thread);
看上圖我們看見這又是一個native方法,此方法的作用是給傳入此方法的thread線程一個許可,代表它可以甦醒了。不調用此方法,如果此方法調用了park方法,他將會一直被阻塞到park操作上。
繼續回到上面的finishCompletion()方法。我們看見代碼中q.thread = null; LockSupport.unpark(t);首先把節點的線程置空,再把線程喚醒,相當於是把節點線程的任務結束掉,再喚醒線程,那麼線程就沒有任務在執行了。繼續往下看能知道後面的代碼就是不斷都往後遍歷節點,並釋放資源。把每個節點的next都置空也是爲了JVM在進行可達性分析算法時,能回收掉相關資源。
在這裏我們不要忘了回到上面的cancel方法。我們也許是從那裏跳過來的。
1.5.2 awaitDone(boolean timed, long nanos)
此方法爲一個等待任務執行結束並返回任務狀態的作用。可以設置最大等待時間,超過等待時間,直接返回任務狀態並結束任務。
/**
* Awaits completion or aborts on interrupt or timeout.
*
* @param timed true if use timed waits
* @param nanos time to wait, if timed
* @return state upon completion
*/
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);
}
}
看以上源碼能得知此方法通過傳入的布爾變量timed判斷是否要設置等待超時參數。timed爲true時,通過當前系統時間加上傳入的nano等待時長,得到死亡線時間,for循環中,先判斷當前線程是否被中斷,中斷則移除任務節點,並報錯。這裏有removeWaiter()方法:請見1.5.3
如果未被中斷,那麼判斷當前狀態是否大於COMPLETING,大於時,返回任務狀態,並釋放線程。意味着當線程執行完成,如下圖,awaitDone方法會返回正常或異常的狀態。
代碼繼續往下看,當前狀態是COMPLETING時,那麼執行Thread.yield();讓線程進入就緒狀態與其他線程再次競爭運行機會。下面判斷queue的狀態,意味for死循環中只會執行一次 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);這段代碼。循環中沒有讓queue再變爲false的代碼。除非CAS失敗,意思就是線程不斷輪訓查看waiters節點的狀態,當任務完成後,就把waiters節點置空,並返回狀態。如果設置了timed狀態爲true,那麼當死亡線時間到達,會直接移除節點任務,並返回任務狀態。最後,當以上所有狀態都不滿足時,此線程會被park方法阻塞。
這裏記得返回get方法。此方法相當於就是對get方法的實現。
1.5.3 removeWaiter(WaitNode node)
這個方法的註釋是:
讓超時的或中斷的節點unlink掉也就是從這個鏈表中取出。來避免累計垃圾。
當不使用CAS時,這些節點時不能分割的,所以當釋放器去遍歷時,是不會對鏈表有影響。
爲避免已刪除的節點分割對未分割節點的影響,鏈表會重新整理。整理的過程是緩慢的,所以鏈表不能過長以致超過整理所計劃消耗的資源。
/**
* Tries to unlink a timed-out or interrupted wait node to avoid
* accumulating garbage. Internal nodes are simply unspliced
* without CAS since it is harmless if they are traversed anyway
* by releasers. To avoid effects of unsplicing from already
* removed nodes, the list is retraversed in case of an apparent
* race. This is slow when there are a lot of nodes, but we don't
* expect lists to be long enough to outweigh higher-overhead
* schemes.
*/
private void removeWaiter(WaitNode node) {
if (node != null) {
node.thread = null;
retry:
for (;;) { // restart on removeWaiter race
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
if (q.thread != null)
pred = q;
else if (pred != null) {
pred.next = s;
if (pred.thread == null) // check for race
continue retry;
}
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
q, s))
continue retry;
}
break;
}
}
}
1.5.4 report(int s)
/**
* Returns result or throws exception for completed task.
*
* @param s completed state value
*/
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
此方法就是獲取任務狀態,並判斷,當任務爲正常完成NORMAL時返回狀態,當任務s大於等於CANCELLED時,那麼也許是4,5,6。當小於CANCELLED時也許是3,爲什麼沒有0和1呢因爲private修飾的report方法在每次調用的時候都已經判斷大於COMPLETING 了 所以。這個report確實是對的,當狀態爲EXCEPTIONAL 就報執行異常ExecutionException((Throwable)x)
並拋出x異常信息,因爲之前我們提到當狀態爲EXCEPTIONAL 時,會把異常信息裝到任務狀態中。可見setException方法。
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;
1.5.5 handlePossibleCancellationInterrupt(int s)
/**
* Ensures that any interrupt from a possible cancel(true) is only
* delivered to a task while in run or runAndReset.
*/
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt
// assert state == INTERRUPTED;
// We want to clear any interrupt we may have received from
// cancel(true). However, it is permissible to use interrupts
// as an independent mechanism for a task to communicate with
// its caller, and there is no way to clear only the
// cancellation interrupt.
//
// Thread.interrupted();
}
1.6 4個protected修飾的方法
1.6.1 set()
/**
* Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
*
* <p>This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
*
* @param v the value
*/
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
看完上面的內容,相信大家對這個UNSAFE裏的方法很熟悉了。那麼set方法就是把NEW狀態更新爲COMPLETING,更新成功就把v結果值賦值給outcome,賦值完成後,再把狀態更新爲NORMAL。最後釋放各種資源調用finishCompletion方法。見1.5.9節。
看完set方法,我們走完了1.3.1中第1種狀態變化。
1.6.2 setException(Throwable t)
/**
* Causes this future to report an {@link ExecutionException}
* with the given throwable as its cause, unless this future has
* already been set or has been cancelled.
*
* <p>This method is invoked internally by the {@link #run} method
* upon failure of the computation.
*
* @param t the cause of failure
*/
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
如果我們看完了set方法再來看此方法,那麼能知道它就是走的1.3.1節中的第二種狀態。
相信看完這裏我們已經走完了1.3.1節任務狀態中的第4種狀態。
1.6.3 done()
此方法是一個回調函數,雖然在本類中此方法沒有任何實現,但是我們仔細觀察會發現,此done方法在本類中被finishCompletion()方法釋放所有資源後最後調用了(請見1.5.1)。然而finishCompletion()又被set方法(請見1.6.1)和setException(Throwable t)和cancel方法(請見1.4.3)調用。這3個方法我們看完就能知道他設置改變了1.3.1中說明會發生的4種狀態變化。從而知道done方法會在每個流程走完的時候被調用,每種流程最後都調用了finishCompletion(),而finishCompletion()最後又會調用done。所以當子類實現此方法後,可以在這方法中寫一些任務執行完需要執行的代碼。finishCompletion()會去回調它。
/**
* Protected method invoked when this task transitions to state
* {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing. Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
* implementation of this method to determine whether this task
* has been cancelled.
*/
protected void done() { }
栗子:task是傳入實現了Runnable或Callable的任務(類)。
FutureTask<Integer> future = new FutureTask<Integer>(task) {
@Override
protected void done() {
try {
System.out.println("任務執行完畢,執行結果爲:" + get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
1.6.4 runAndReset()
此方法從名字上很好懂,運行並重置。此方法在本類中沒有被任何其他方法調用。
先來看看它的註釋:
在不設置結果的情況下執行計算,然後將此任務重置爲初始狀態,如果計算遇到異常或被取消,則無法執行此操作。這是爲原本會執行多次的任務的需求而設計的。
意思就是當一個任務執行之後會改變一些運行前的環境,而不能重複運行任務,而這個方法就是保證每次run正常執行後,都能重置並再次運行。
/**
* Executes the computation without setting its result, and then
* resets this future to initial state, failing to do so if the
* computation encounters an exception or is cancelled. This is
* designed for use with tasks that intrinsically execute more
* than once.
*
* @return {@code true} if successfully run and reset
*/
protected boolean runAndReset() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// 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
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
一下代碼我們在run方法中解釋過了,是保證當前只有一個線程在運行任務,不論換哪一個線程來執行任務,任務都只有一個任務。如果當前沒有線程執行本任務,那麼把任務類中記錄的運行本任務線程的變量置爲本線程(此變量被存放在本類偏移量爲runnerOffset的位置)。就是說沒有線程運行本任務那麼就由我來。
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
此方法對比run方法,他關鍵的地方就是沒有調用set方法。而set方法做了啥呢(參見1.6.1節)?他相當於讓任務狀態進行了 4種狀態的第一種:
NEW -> COMPLETING -> NORMAL
如果不調用set方法的話,任務狀態始終是NEW。所以我們能知道。runAndReset()的核心功能是,在運行完成後並不改變此任務的狀態。以此達到多次運行的效果,比如當前有個需求是運行此任務4次算完成任務。那麼前3次我可以調用runAndReset(),最後以此調用run(),從run方法或runAndReset中我們是能知道只有當狀態爲NEW時,任務纔會運行的。其他狀態時都會直接return了。
補充一下,爲什麼其他狀態我們不考慮呢?因爲其他狀態要麼是中斷要麼就是取消了,要麼是異常了。只有正常運行完成我們纔會繼續任務對吧。因此纔會在返回時有判斷 return ran && s == NEW;代表ran要是true運行成功,並且沒有被取消和中斷。s需要判斷爲NEW纔算reset。
1.7 靜態代碼塊
// 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"));//獲取當前k類中任務的狀態屬性偏移量
runnerOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));//獲取當前執行Callable方法的線程的偏移量
waitersOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("waiters"));//獲取k類的waiters等待線程節點偏移量
} catch (Exception e) {
throw new Error(e);
}
}
上面代碼中可以看出,先獲取到了FutureTask類的Class對象k。然後獲取它的state屬性,runner屬性,waiters屬性,的偏移量。這三個屬性都能在類中源碼看見。
1.8 靜態內部類
本類中執行任務的線程等待鏈表的一個節點的構造類。
/**
* Simple linked list nodes to record waiting threads in a Treiber
* stack. See other classes such as Phaser and SynchronousQueue
* for more detailed explanation.
*/
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
二.類的使用實踐
待完善...