理解Android多執行緒裡面三種任務Runnable和Callable和FutureTask的用法
阿新 • • 發佈:2019-01-05
理解三種任務Runnable和Callable和FutureTask的用法
1.Runnable 和Callable和FutureTask的區別
相同點:都屬於執行緒池中要被執行的任務; 不同點: Runnable是無返回值的任務,可以線上程中使用 Callable是有返回值的任務 ,不可以線上程中使用 FutureTask是有返回值,而且更易於管理和控制的任務,不可以線上程中使用; 前兩者通過檢視他們類可以很清楚的知道public interface Runnable {
/**這個任務執行完之後沒有返回值*/
public abstract void run();
}
而FutureTask稍微複雜一點,其實看看它的類結構 ,你也就明白了怎麼回事 看到了嗎,它也是一個Runnable的子類,這裡還用到了一個Future類,其實想一下,Runnable是不可控制的任務,Future為這個任務提供了一套標準來管理任務;不信看看它的類public interface Callable<V> { /**這個任務執行完之後返回泛型 V*/ V call() throws Exception; }
public interface Future<V> { /**取消這個任務執行,傳遞引數是true,表示停止正在執行的任務,否則,這行完這次任務*/ boolean cancel(boolean mayInterruptIfRunning); /**任務是否被取消*/ boolean isCancelled(); /**任務是否完成*/ boolean isDone(); /**獲取任務的返回結果*/ V get() throws InterruptedException, ExecutionException; /**獲取任務的結果,還沒有完成就等待*/ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
2.Runnable和Callable和FutureTask的執行
執行緒池ExecutorService裡面提交任務的方法submit有如下三個過載,再來看看AbstractExecutorService類中三種submit方法原始碼的執行吧
這三個方法都會: 1.先通過newTaskFor方法生成一個RunnableFuture的物件. 2.然後在執行execute方法.public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
1.生成newTaskFor方法過程
在看看newTaskFor方法是怎麼回事; protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
原來它返回的是FutureTask型別,原來不論我們傳遞到submit方法中的是那種任務,先是轉換成FutureTask型別;再來看看FutureTask的構造方法吧!
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
sync = new Sync(callable);
}
public FutureTask(Runnable runnable, V result) {
sync = new Sync(Executors.callable(runnable, result));
}
它構造出了一個 Sync物件,看看它的構造方法,
Sync(Callable<V> callable) {
this.callable = callable;
}
傳遞進去的是Callable型別的,即使是Runable也會被轉化為Callable型別,我們在看看 Excutors.call()這個方法,它是將Runable轉化為一個Callable
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, 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;
}
}
Excutors.call這個方法會先將生成一個 RunnableAdapter,而這個類中含有Runnable的應用;
到此 submit中提交的任務最終都會被轉化為Callable型別的任務;
梳理一下 我們傳遞進來的不論是Runable型別還是Callable型別任務,都統一構造成FutureTask型別(只接受Callable型別引數),如果是Runable型別,而FutureTask構造內部會將它轉為會Callable型別的變數傳入FutureTask的構造;2.execute方法的執行
到這裡excute方法要執行的這個FutureTask物件,我們先想一下它又是什麼?上面我們已經提到了,它呀實現了Runnaable介面,不就是一個執行緒嗎?我們要執行的是一個執行緒!執行緒要看什麼不就是run()方法?那麼現在我們直接看看FutureTask類中實現的run方法不就是了;當然execute內部執行不看了,反正它是一個執行緒,是執行緒就要執行run()方法對嗎? 看看FutureTask中的run()方法的原始碼 public void run() {
sync.innerRun();
}
看看就它一行程式碼,什麼也不說了;往下繼續走吧!
void innerRun() {
if (!compareAndSetState(READY, RUNNING))
return;
runner = Thread.currentThread();
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
result = callable.call();
} catch (Throwable ex) {
setException(ex);
return;
}
set(result);
} else {
releaseShared(0); // cancel
}
}
看到那一行 result = callable.call()程式碼了嗎,當執行緒處於執行狀態的時候會呼叫它submit提交任務的時候 FutureTask中持有Sync引用 ,而Sync中又持有Callable的引用,而此處的callable不就是我們的傳遞進來的Callable型別的物件,或者是Runnable型別的物件轉換成的Callable嗎; 現在好辦了
1.如果是我們自己實現了Callable介面,那麼此處就會直接呼叫我們overvide的call()方法;看程式碼示例
ExecutorService mExecutorService = Executors.newCachedThreadPool();
mExecutorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("我直接實現了Callable介面");
return "111";
}
});
此時會直接呼叫自己的實現的call方法;列印 System.out.println的內容
2.那麼我是實現了Runnable介面的情況,那麼再看分析吧!
是Runable的話,會被Excutors.call方法轉變成Callable物件使用;當此處呼叫result = callable.call()的時候,這個callable是轉化來的。這個callable又是怎麼回事?上面也提到了,這裡再看一眼Excutors.call是怎麼轉化的; public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, 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;
}
}
轉化成的RunnableAdapter物件也是一個Callable的實現類; 這個時候會呼叫自己實現的call()方法;看看call()方法的內部task.run()這行程式碼 ,task會指向我們的Runable的物件;
3.那麼如果我submit中傳遞進去的FutureTask呢!
按照我上面的Callable和Runnable套路分析,你會發現這全都是套路!在走一遍套路,先看一下例子程式碼; FutureTask<String> m1 = new FutureTask<String>(
new Callable<String>() {
@Override
public String call() throws Exception {
return null;
}
});
mExecutorService.submit(m1);
FutureTask<String> m2 = new FutureTask<String>(
new Runnable() {
@Override
public void run() {
}
}, null);
mExecutorService.submit(m2);
上面是FutureTask的兩種構造;一個傳遞進去了Callable,另一個傳遞進去了Runable了;
分析
首先FutureTask實現了Runnable介面!
那麼m1和m2他們都會被當成是Runnable引數構造成一個新的FutureTask任務,執行的時候會按照2.那麼我是實現了Runnable介面的情況,那麼再看分析吧!這種情形分析的執行吧.
那麼我這個m1和m2分別執行自己的run()方法,但是自己沒有覆寫run()方法,m1和m2都是FutureTask的物件。這時候你會發現
m1按照1.如果是我們自己實現了Callable介面,那麼此處就會直接呼叫我們overvide的call()方法;看程式碼示例
套路走!
m2按照2.那麼我是實現了Runnable介面的情況,那麼再看分析吧!套路走!
是不是有點遞迴的感覺的,,,差點被繞暈了!
以上就是我對三種任務的理解,希望對大家有所幫助!