1. 程式人生 > >java中Future與FutureTask使用與分析

java中Future與FutureTask使用與分析

strac 構造 沒有 設置 就是 wap 什麽 其中 實例化

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

一、Future與FutureTask介紹

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

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個方法,下面介紹一下每個方法的作用:

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

1、如果任務已經完成,則無論mayInterruptIfRunning為true還是false,都返回false,這是因為你要取消的任務已經完成,則認為取消任務失敗;

2、如果任務正在執行,則無論mayInterruptIfRunning為true還是false,都返回true。只不過mayInterruptIfRunning為true時線程會被中斷,false時線程不會被中斷會執行完。

3、如果任務還沒有執行,則無論mayInterruptIfRunning為true還是false,都返回true。

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使用與分析

1、使用Future時,我們需要實現Callable接口,並通過ExecutorService接口的submit方法獲取返回的Future對象,

2、使用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對象,所以我們具體分析下上面代碼中的三種情況:

1、如果你傳入的是自己實現的Runaable類或者Callable類,那麽sumbit方法自然會幫你自動封裝為FutureTask對象,運行後通過Future對象獲取結果。

2、你傳入的已經是個自己構造的FutureTask對象,由於FutureTask其實是實現了Runnable接口的,它本身就是個Runaable實現類, sumbit方法還是會將它視為Runnable類來進行封裝,並最終會執行FutureTask自己的run方法,一系列實現都在你傳入的FutureTask對象內完成,所以你可以直接通過自己構建的FutureTask獲取結果;

3、自己單獨聲明線程運行,跟第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兩者關系的一個大致的介紹與分析,其中如有不足與不正確的地方還望指出與海涵。

java中Future與FutureTask使用與分析