AsyncTask分析(二)—Future、Callable、FutureTask
之前分析AsyncTask源碼的時候,在其中提到了Future,Callable,FutureTask三個類或接口,今天就揭開他們的神祕面紗,探究他們的運用。
先看一下Future接口源碼:
//Future是一個接口,他提供給了我們方法來檢測當前的任務是否已經結束
//,還可以等待任務結束並且拿到一個結果,說白了他可以用來進行線程同步
public interface Future<V> {
//如果任務已經完成或者已經停止了或者這個任務無法停止,則會返回一個false
boolean cancel(boolean mayInterruptIfRunning);
//判斷任務是否取消,
boolean isCancelled();
//判斷任務是否完成
boolean isDone();
//取值,任務未完成之前,阻塞當前線程,直到任務完成,
//如果已經調用cancel(),在調用gete,會拋異常
V get() throws InterruptedException, ExecutionException;
//設置最大等待時間(線程阻塞最長時間),其餘同get()
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
幹說,可能不太好理解,我們來個Demo,測試一下
private static void executor() throws Exception{
ExecutorService service = Executors.newSingleThreadExecutor();
CountRunnable count = new CountRunnable();
Future f = service.submit(count);
long start = System.currentTimeMillis();
System.out.println("start:"+start);
try{
System.out.println("result:"+f.get(10*1000, TimeUnit.MILLISECONDS));
}catch(Exception e){
System.out.println(e.toString());
}finally{
service.shutdown();
}
long end = System.currentTimeMillis();
System.out.println("任務結束 end:"+end+",共耗時:"+(end-start));
}
static class CountRunnable implements Runnable{
private int sum;
@Override
public void run() {
for(int i=1 ; i<11 ; i++){
sum = sum+i;
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("run 結束 sum="+sum);
}
}
public static void main(String[] args) {
try {
executor();
} catch (Exception e) {
e.printStackTrace();
}
}
看下打印結果:
Future.get()方法的的確確是阻塞了當前線程(因此如果在Android上使用Future不能再UIThread調用Future.get();),任務線程的完成時間是20s,但是我們的get()設置的阻塞時間是10s,在10s內無法返回結果,就會報TimeOut異常。爲什麼 任務線程最後輸出還是sum=55?那是因爲我們並沒有取消任務,任務繼續執行。
繼續來看Callable:
//同樣是一個泛型接口,只有一個call()
public interface Callable<V> {
V call() throws Exception;
}
這個是用來做什麼的呢?看過文檔註釋就會明白,在普通的Runnable執行過程中,也就是run()執行是沒有返回值的,那我們也就無法判斷任務的執行情況,也沒法決定在什麼時候停止任務,取消任務,但是Callable call()就給我們提供了這個判斷。
看一個Demo
static void callTest(){
ExecutorService pool = Executors.newSingleThreadExecutor();
CountCallable count = new CountCallable();
Future<Number> f = pool.submit(count);
long start = System.currentTimeMillis();
System.out.println("start:"+start);
try{
System.out.println("result:"+f.get().number);
}catch(Exception e){
System.out.println(e.toString());
}finally{
pool.shutdown();
}
long end = System.currentTimeMillis();
System.out.println("任務結束 end:"+end+",共耗時:"+(end-start));
}
static class CountCallable implements Callable<Number>{
@Override
public Number call() throws Exception {
Number number = new Number();
number.setNumber(100);
System.out.println("call...");
TimeUnit.SECONDS.sleep(2);
return number;
}
}
static class Number{
public int number;
public void setNumber(int number){
this.number = number;
}
}
看下結果:
當pool.submit()之後,就會立即回調call(),當任務完成之後,就會返回我們在call()中設置的Number值。
剛剛說了,Future還有個功能是取消任務,不妨來試一下:
static void cancelTest() throws Exception{
ExecutorService pool = Executors.newSingleThreadExecutor();
CountCallable count = new CountCallable();
Future<Number> f = pool.submit(count);
System.out.println("任務開始於:"+System.currentTimeMillis());
TimeUnit.SECONDS.sleep(4);
f.cancel(true);
if(f.isCancelled()){
System.out.println("任務被取消於:"+System.currentTimeMillis());
}else{
Number b = f.get();
System.out.println("任務繼續執行:");
if(f.isDone()){
System.out.println("任務執行完畢:"+System.currentTimeMillis());
pool.shutdown();
}
}
}
看一下打印結果:
,cancel()之後,是不能再調get(),不然會報異常。
到這裏Future和Callable基本就介紹完了,下面再看一下FutureTask
FutureTask登場
Future是一個接口,他的唯一實現類就是FutureTask,其實FutureTask的一個很好地特點是他有一個回調函數done()方法,當一個任務執行結束後,會回調這個done()方法,我們可以在done()方法中調用FutureTask的get()方法來獲得計算的結果。爲什麼我們要在done()方法中去調用get()方法呢? 這是有原因的,我在Android開發中,如果我在主線程去調用futureTask.get()方法時,會阻塞我的UI線程,如果在done()方法裏調用get(),則不會阻塞我們的UI線程。
先看一下
public class FutureTask<V> implements RunnableFuture<V>{}
public interface RunnableFuture<V> extends Runnable, Future<V> {}
這就知道了FutureTask,即可作爲Runnable,也可作爲Future;
那就看下FutureTask中的代碼:
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, 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);
}
}
protected void setException(Throwable t) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = t;
U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state
finishCompletion();
}
}
當執行FutureTask中的run()時,真正起作用的其實是Callable接口在調用call(),這裏最終也是調用了finishCompletion();
再來看一段:
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
U.compareAndSwapInt(this, STATE, 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
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, 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
}
這裏我們看到,當調用cancel(boolean)後,如果mayInterruptIfRunning = true,就會去執行t.interrupt();不論成功與否,最後執行finishCompletion(),執行done(),置空callable;
所以不論是cancel(返回true的時候),還是run最終都會調用done()
來個Demo驗證下:
static class CountCallable implements Callable<Number>{
@Override
public Number call() throws Exception {
Number n = new Number();
n.number = 100;
System.out.println("call....");
TimeUnit.SECONDS.sleep(10);
return n;
}
}
static class Number{
public int number;
}
FutureTask<Number> task ;
ExecutorService es;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CountCallable callable = new CountCallable();
task = new FutureTask<Number>(callable){
@Override
protected void done() {
try {
Number result = task.get();
System.out.println("任務結束 number:"+result.number+",結束:"+System.currentTimeMillis()+",isDone:"+task.isCancelled()+",isCancel:"+task.isCancelled()+",theadName:"+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
};
es = Executors.newFixedThreadPool(2);
es.execute(task);
System.out.println("任務開始:"+System.currentTimeMillis());
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(task.isDone()||task.isCancelled()){
return;
}
System.out.println(task.cancel(true)+",isCanceled:"+task.isCancelled());
}
});
}
看下結果:
1,不點擊cancel:
爲毛要在done()裏面調用get(),因爲done()是運行在子線程的,不會阻塞UIThread,
2,點擊cancel:
task.cancel()返回true;
那在回過頭來看下AsyncTask中的運用:
public AsyncTask() {
//Callable接口的實現類,也就是FutureTask run()中真正工作的部分
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 暴露到程序中的就是doInBackGround();
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
//這裏獲取的就是call()中的result
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
//同樣交給了Future去處理
return mFuture.cancel(mayInterruptIfRunning);
}
我們重點追蹤execute()這個流程:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
//這裏其實就是SerialExecutor的execute()
exec.execute(mFuture);
return this;
}
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
一步一步追蹤下來,發現最終都是在SerialExecutor裏面了,exec.execute(mFuture)其實就是SerialExecutor執行的execute(mFuture);
這裏面我們看到直接調用了mFuture.run();
執行的流程圖如下: