java中Future與FutureTask使用與分析

Future與FutureTask都是用於獲取線程執行的返回結果。下面咱們就對二者之間的關係與使用進行一個大體的介紹與分析java

1、Future與FutureTask介紹函數

Future位於java.util.concurrent包下,它是一個接口this

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接口中聲明瞭5個方法,下面介紹一下每一個方法的做用:spa

cancel方法用來取消任務,取消成功則返回true,取消失敗則返回false。參數mayInterruptIfRunning設置爲false,表示不容許在線程運行時中斷,設置爲true則表示容許。具體可分爲如下三種狀況:線程

        一、若是任務已經完成,則不管mayInterruptIfRunning爲true仍是false,都返回false,這是由於你要取消的任務已經完成,則認爲取消任務失敗;code

        二、若是任務正在執行,則不管mayInterruptIfRunning爲true仍是false,都返回true。只不過mayInterruptIfRunning爲true時線程會被中斷,false時線程不會被中斷會執行完。對象

        三、若是任務尚未執行,則不管mayInterruptIfRunning爲true仍是false,都返回true。blog

isCancelled方法用於判斷任務是否被取消成功,cancel方法成功則返回 true,反之則爲false。繼承

isDone用於判斷任務是否完成,  若是任務完成則返回true。任務完成包括正常結束、任務被取消、任務發生異常,都返回true接口

get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;

get(long timeout, TimeUnit unit)用來獲取執行結果,若是在指定時間內,還沒獲取到結果,拋出 java.util.concurrent.TimeoutException 異常 

FutureTask 實現了RunnableFuture接口,而RunnableFuture則繼承了Future<V>與Runnable接口,因此 FutureTask不只實現了 Future<V>接口的全部方法,還具備本身的run方法,咱們能夠看下它的類圖

、Future與FutureTask使用與分析

一、使用Future時,咱們須要實現Callable接口,並經過ExecutorService接口的submit方法獲取返回的Future對象,

二、使用FutureTask時,根據FutureTask的構造函數能夠看到FutureTask既能夠接收Callable的實現類,也能夠接收Runnable的實現類。當你傳入的是Callable的實現類時,能夠獲取線程執行的結果;傳入Runnable的實現類時,因爲Runnable的實現沒有返回值,須要傳入一個你設置的線程完成標識,也就是result,而後當線程結束時會把你傳入的result原值返回給你,FutureTask的構造函數具體以下:

    public class FutureTask<V> implements RunnableFuture<V>public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
     }

     public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);//runnable轉化爲callable 
        this.state = NEW;       // ensure visibility of callable
     }
}

 接下來咱們看下Future與FutureTask具體的使用代碼:

        // 執行任務 實現Runnable
        FutureTaskJobRunnable taskRun = new FutureTaskJobRunnable();
        // 執行任務 實現Callable
        FutureTaskJobCallable taskCall = new FutureTaskJobCallable();
        String val = "ok";
        // 線程運行成功後把,返回你傳入的val值
        FutureTask<String> futureTaskRun = new FutureTask<String>(taskRun, val);
        // 線程運行,返回線程執行的結果
        FutureTask<String> futureTaskCall = new FutureTask<String>(taskCall);
        
        //聲明線程池
        ExecutorService executor = Executors.newCachedThreadPool();        
        //Future
        Future<String> future =  executor.submit(taskCall);
        System.out.println(future.get());
        //FutureTask
        executor.submit(futureTaskCall);
        System.out.println(futureTaskCall.get());
        //FutureTask自定義線程執行
        new Thread(futureTaskRun).start();
        System.out.println(futureTaskRun.get());
public class FutureTaskJobCallable implements Callable<String>{
    
    public String call() throws Exception {
        System.out.println("FutureTaskJobCallable已經執行了哦");
        Thread.sleep(1000);
        return "返回結果";
    }

}

public class FutureTaskJobRunnable implements Runnable {
    
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("FutureTaskJobRunnable已經執行了哦");
    }

}

根據上面的代碼咱們從ExecutorService接口中submit方法入手,看下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;
    }

    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);
    }

能夠看到當你使用submit方法提交任務時,都會經過newTaskFor方法轉換成FutureTask對象,因此咱們具體分析下上面代碼中的三種狀況:

一、若是你傳入的是本身實現的Runaable類或者Callable類,那麼sumbit方法天然會幫你自動封裝爲FutureTask對象,運行後經過Future對象獲取結果。

二、你傳入的已是個本身構造的FutureTask對象,因爲FutureTask實際上是實現了Runnable接口的,它自己就是個Runaable實現類, sumbit方法仍是會將它視爲Runnable類來進行封裝,並最終會執行FutureTask本身的run方法,一系列實現都在你傳入的FutureTask對象內完成,因此你能夠直接經過本身構建的FutureTask獲取結果;

三、本身單獨聲明線程運行,跟第2點相似,FutureTask自己就是個Runnabel實現類,天然能夠作爲參數傳入Thread運行;

那麼咱們把自定義的Runnable、Callable實現類作爲參數構造FutureTask後,FuttureTask是如何運行的呢,咱們能夠看下FuttureTask中具體的代碼實現

//你傳入的Runnable與Callable實現類都會在構造函數中轉化爲Callable
private Callable<V> callable;

    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);
        }
    }

能夠看到FutureTask類自己的run方法,就是執行Runnable、Callable的實現類並獲取返回結果的過程。

因此ExecutorService接口中submit方法歸根結底仍是要把你傳入的對象封裝成FutureTask對象,並經過FutureTask類的內部實現來獲取結果的,返回的Future接口對象也要依賴於FutureTask實例化的,因此不管是直接傳入本身的Runnable、Callable實現類仍是構建FutureTask傳入,本質上都是經過FutureTask去實現,沒有什麼區別;

以上就是對Future與FutureTask二者關係的一個大體的介紹與分析,其中若有不足與不正確的地方還望指出與海涵。

相關文章
相關標籤/搜索