java.util.concuttent Callable Future詳解

在傳統的多線程實現方式中(繼承Thread和實現Runnable)沒法直接獲取線程執行的返回結果,若是須要獲取執行結果,就必須經過共享變量或者使用線程通訊的方式來達到效果,這樣使用起來就比較麻煩。java

從Java 1.5開始,java.util.concurrent包中提供了 Callable和 Future兩個接口,經過它們就能夠在任務執行完畢以後獲得任務執行結果。程序員

Callable

Callable與Runnable的功能大體類似,Callable中有一個call()函數,可是call()函數有返回值,而Runnable的run()函數不能將結果返回給客戶程序。多線程

Future

Executor就是Runnable和Callable的調度容器,Future就是對於具體的Runnable或者Callable任務的執行結果進行框架

取消、查詢是否完成、獲取結果、設置結果操做。get方法會阻塞,直到任務返回結果。dom

 

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;
}
  • cancel方法用來取消任務,若是取消任務成功則返回true,若是取消任務失敗則返回false。參數mayInterruptIfRunning表示是否容許取消正在執行卻沒有執行完畢的任務,若是設置true,則表示能夠取消正在執行過程當中的任務。若是任務已經完成,則不管mayInterruptIfRunning爲true仍是false,此方法確定返回false,即若是取消已經完成的任務會返回false;若是任務正在執行,若mayInterruptIfRunning設置爲true,則返回true,若mayInterruptIfRunning設置爲false,則返回false;若是任務尚未執行,則不管mayInterruptIfRunning爲true仍是false,確定返回true。
  • isCancelled方法表示任務是否被取消成功,若是在任務正常完成前被取消成功,則返回 true。
  • isDone方法表示任務是否已經完成,若任務完成,則返回true;
  • get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
  • get(long timeout, TimeUnit unit)用來獲取執行結果,若是在指定時間內,還沒獲取到結果,就直接返回null。

也就是說Future提供了三種功能:異步

  • 判斷任務是否完成
  • 可以中斷任務
  • 可以獲取任務的執行結果

Future和FutureTask區別

Future是一個接口,  FutureTask類是Future 的一個實現類,並實現了Runnable,所以FutureTask能夠傳遞到線程對象Thread中新建一個線程執行。因此可經過Excutor(線程池) 來執行,也可傳遞給Thread對象執行。若是在主線程中須要執行比較耗時的操做時,但又不想阻塞主線程時,能夠把這些做業交給Future對象在後臺完成,當主線程未來須要時,就能夠經過Future對象得到後臺做業的計算結果或者執行狀態。 

FutureTask是爲了彌補Thread的不足而設計的,它可讓程序員準確地知道線程何時執行完成並得到到線程執行完成後返回的結果(若是有須要)。ide

FutureTask是一種能夠取消的異步的計算任務。它的計算是經過Callable實現的,它等價於能夠攜帶結果的Runnable,而且有三個狀態:等待、運行和完成。完成包括全部計算以任意的方式結束,包括正常結束、取消和異常。函數

Executor框架利用FutureTask來完成異步任務,並能夠用來進行任何潛在的耗時的計算。通常FutureTask多用於耗時的計算,主線程能夠在完成本身的任務後,再去獲取結果。測試

FutureTask多用於耗時的計算,主線程能夠在完成本身的任務後,再去獲取結果線程

Example1

package cn.com.example.concurrent.future;

import java.util.concurrent.*;

/**
 * Created by Jack on 2017/1/24.
 */
public class RunnableFutureTask {

    /**
     * ExecutorService
     */
    static ExecutorService mExecutor = Executors.newSingleThreadExecutor();

    /**
     * @param args
     */
    public static void main(String[] args) {
        runnableDemo();
        futureDemo();
    }

    /**
     * runnable, 無返回值
     */
    static void runnableDemo() {

        new Thread(new Runnable() {

            @Override
            public void run() {
                System.out.println("runnable demo : " + fibc(20));
            }
        }).start();
    }

    /**
     * 其中Runnable實現的是void run()方法,無返回值;Callable實現的是 V
     * call()方法,而且能夠返回執行結果。其中Runnable能夠提交給Thread來包裝下
     * ,直接啓動一個線程來執行,而Callable則通常都是提交給ExecuteService來執行。
     */
    static void futureDemo() {
        try {
            /**
             * 提交runnable則沒有返回值, future沒有數據
             */
            Future<?> result = mExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    fibc(20);
                }
            });

            System.out.println("future result from runnable : " + result.get());

            /**
             * 提交Callable, 有返回值, future中可以獲取返回值
             */
            Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return fibc(20);
                }
            });

            System.out.println("future result from callable : " + result2.get());

            /**
             * FutureTask則是一個RunnableFuture<V>,即實現了Runnbale又實現了Futrue<V>這兩個接口,
             * 另外它還能夠包裝Runnable(實際上會轉換爲Callable)和Callable
             * <V>,因此通常來說是一個符合體了,它能夠經過Thread包裝來直接執行,也能夠提交給ExecuteService來執行
             * ,而且還能夠經過v get()返回執行結果,在線程體沒有執行完成的時候,主線程一直阻塞等待,執行完則直接返回結果。
             */
            FutureTask<Integer> futureTask = new FutureTask<Integer>(
                    new Callable<Integer>() {
                        @Override
                        public Integer call() throws Exception {
                            return fibc(20);
                        }
                    });
            // 提交futureTask
            mExecutor.submit(futureTask);
            System.out.println("future result from futureTask : " + futureTask.get());

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * 效率底下的斐波那契數列, 耗時的操做
     *
     * @param num
     * @return
     */
    static int fibc(int num) {
        if (num == 0) {
            return 0;
        }
        if (num == 1) {
            return 1;
        }
        return fibc(num - 1) + fibc(num - 2);
    }

}

結果:

runnable demo : 6765
future result from runnable : null
future result from callable : 6765
future result from futureTask : 6765

Example2:

package cn.com.example.concurrent.future;

import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by Jack on 2017/1/24.
 */
public class RunnableFutureTask {

    public static void main(String[] args) {
        // 初始化一個Callable對象和FutureTask對象
        Callable pAccount = new PrivateAccount();
        FutureTask futureTask = new FutureTask(pAccount);
        // 使用futureTask建立一個線程
        Thread pAccountThread = new Thread(futureTask);
        System.out.println("futureTask線程如今開始啓動,啓動時間爲:" + System.nanoTime());
        pAccountThread.start();
        System.out.println("主線程開始執行其餘任務");
        // 從其餘帳戶獲取總金額
        int totalMoney = new Random().nextInt(100000);
        System.out.println("如今你在其餘帳戶中的總金額爲" + totalMoney);
        System.out.println("等待私有帳戶總金額統計完畢...");
        // 測試後臺的計算線程是否完成,若是未完成則等待
        while (!futureTask.isDone()) {
            try {
                Thread.sleep(500);
                System.out.println("私有帳戶計算未完成繼續等待...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("futureTask線程計算完畢,此時時間爲" + System.nanoTime());
        Integer privateAccountMoney = null;
        try {
            privateAccountMoney = (Integer) futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("您如今的總金額爲:" + totalMoney + privateAccountMoney.intValue());
    }
}

class PrivateAccount implements Callable {
    Integer totalMoney;

    @Override
    public Object call() throws Exception {
        Thread.sleep(5000);
        totalMoney = new Integer(new Random().nextInt(10000));
        System.out.println("您當前有" + totalMoney + "在您的私有帳戶中");
        return totalMoney;
    }

}

結果:

futureTask線程如今開始啓動,啓動時間爲:88171383410225
主線程開始執行其餘任務
如今你在其餘帳戶中的總金額爲2838
等待私有帳戶總金額統計完畢...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
私有帳戶計算未完成繼續等待...
您當前有9238在您的私有帳戶中
私有帳戶計算未完成繼續等待...
futureTask線程計算完畢,此時時間爲88176389195218
您如今的總金額爲:28389238
相關文章
相關標籤/搜索