1. 程式人生 > >理解Android多執行緒裡面三種任務Runnable和Callable和FutureTask的用法

理解Android多執行緒裡面三種任務Runnable和Callable和FutureTask的用法

理解三種任務Runnable和Callable和FutureTask的用法

1.Runnable 和Callable和FutureTask的區別

相同點:都屬於執行緒池中要被執行的任務; 不同點: Runnable是無返回值的任務,可以線上程中使用 Callable是有返回值的任務 ,不可以線上程中使用 FutureTask是有返回值,而且更易於管理和控制的任務,不可以線上程中使用; 前兩者通過檢視他們類可以很清楚的知道
public interface Runnable {
   /**這個任務執行完之後沒有返回值*/
    public abstract void run();
}
public interface Callable<V> {
   /**這個任務執行完之後返回泛型 V*/
    V call() throws Exception;
}
而FutureTask稍微複雜一點,其實看看它的類結構 ,你也就明白了怎麼回事 看到了嗎,它也是一個Runnable的子類,這裡還用到了一個Future類,其實想一下,Runnable是不可控制的任務,Future為這個任務提供了一套標準來管理任務;不信看看它的類
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有如下三個過載,


而在ExecutorService的抽象實現類AbstractExecutorService裡面對著三個submit方法做了實現,你會發現他們都呼叫了Executor介面中的execute(Runnable command)方法; 至於這三個類的關係,看下圖就知道,他們是繼承關係
再來看看AbstractExecutorService類中三種submit方法原始碼的執行吧
 
    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方法生成一個RunnableFuture的物件. 2.然後在執行execute方法.

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介面的情況,那麼再看分析吧!套路走! 是不是有點遞迴的感覺的,,,差點被繞暈了! 以上就是我對三種任務的理解,希望對大家有所幫助!