【小家Java】Future與FutureTask的區別與聯繫

【小家java】java5新特性(簡述十大新特性) 重要一躍
【小家java】java6新特性(簡述十大新特性) 雞肋升級
【小家java】java7新特性(簡述八大新特性) 不溫不火
【小家java】java8新特性(簡述十大新特性) 飽受讚譽
【小家java】java9新特性(簡述十大新特性) 褒貶不一
【小家java】java10新特性(簡述十大新特性) 小步迭代
【小家java】java11新特性(簡述八大新特性) 首個重磅LTS版本java

Future模式簡述
傳統單線程環境下,調用函數是同步的,必須等待程序返回結果後,纔可進行其餘處理。 Futrue模式下,調用方式改成異步。
Futrue模式的核心在於:充分利用主函數中的等待時間,利用等待時間處理其餘任務,充分利用計算機資源。
所謂異步調用其實就是實現一個可無需等待被調用函數的返回值而讓操做繼續運行的方法。在 Java 語言中,簡單的講就是另啓一個線程來完成調用中的部分計算,使調用繼續運行或返回,而不須要等待計算結果。但調用者仍須要取線程的計算結果。編程

JDK5新增了Future接口,用於描述一個異步計算的結果。雖然 Future 以及相關使用方法提供了異步執行任務的能力,可是對於結果的獲取倒是很不方便,只能經過阻塞或者輪詢的方式獲得任務的結果。阻塞的方式顯然和咱們的異步編程的初衷相違背,輪詢的方式又會耗費無謂的 CPU 資源,並且也不能及時地獲得計算結果。異步

Future
Future就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果等操做。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。
Future類位於java.util.concurrent包下,它是一個接口:異步編程

public interface Future<V> {
    /**
     * 方法用來取消任務,若是取消任務成功則返回true,若是取消任務失敗則返回false。
     *
     * @param mayInterruptIfRunning 表示是否容許取消正在執行卻沒有執行完畢的任務,若是設置true,則表示能夠取消正在執行過程當中的任務。
     * @return 若是任務已經完成,則不管mayInterruptIfRunning爲true仍是false,此方法確定返回false,即若是取消已經完成的任務會返回false;
     * 若是任務正在執行,若mayInterruptIfRunning設置爲true,則返回true,若mayInterruptIfRunning設置爲false,則返回false;
     * 若是任務尚未執行,則不管mayInterruptIfRunning爲true仍是false,確定返回true。
     */
    boolean cancel(boolean mayInterruptIfRunning);函數

    /**
     * 方法表示任務是否被取消成功
     * @return 若是在任務正常完成前被取消成功,則返回 true
     */
    boolean isCancelled();this

    /**
     * 方法表示任務是否已經完成
     * @return 若任務完成,則返回true
     */
    boolean isDone();.net

    /**
     * 方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回
     * @return 任務執行的結果值
     * @throws InterruptedException
     * @throws ExecutionException
     */
    V get() throws InterruptedException, ExecutionException;線程

    /**
     * 用來獲取執行結果,若是在指定時間內,還沒獲取到結果,就直接返回null(並非拋出異常,須要注意)。
     * @param timeout 超時時間
     * @param unit 超時單位
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     * @throws TimeoutException
     */
    V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
從上面方法的註釋能夠看出,Futrue提供了三種功能:
1)判斷任務是否完成;
2)可以中斷任務;
3)可以獲取任務執行結果。(最爲經常使用的)對象

由於Future只是一個接口,因此是沒法直接用來建立對象使用的,所以就有了下面的FutureTask(JDK8之前惟一實現類)。blog

FutureTask
public class FutureTask<V> implements RunnableFuture<V>
    public interface RunnableFuture<V> extends Runnable, Future<V>
1
2
由此看出FutureTask它既能夠做爲Runnable被線程執行,又能夠做爲Future獲得Callable的返回值。
FutureTask提供了2個構造器:

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

    //把Runable轉換成callable來處理(結果盡然讓傳進來,因此這個方法沒啥用)
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
FutureTask實現了Runnable,所以它既能夠經過Thread包裝來直接執行,也能夠提交給ExecuteService來執行。
FutureTask實現了Futrue能夠直接經過get()函數獲取執行結果,該函數會阻塞,直到結果返回。
舉個栗子
直接使用Futrue來接收返回值:(結合callable)
    public static void main(String args[]) {
        Instant start = Instant.now();

        ExecutorService executor = Executors.newCachedThreadPool();
        //執行callable任務  拿到但繪製result
        Future<Integer> result = executor.submit(() -> {
            System.out.println("子線程在進行計算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        });
        Instant mid = Instant.now();
        System.out.println("Mid拿到Futrue結果對象result:" + Duration.between(start, mid).toMillis());

        try {
            System.out.println("task運行結果計算的總和爲:" + result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        executor.shutdown();

        Instant end = Instant.now();
        System.out.println("End拿到結果值:" + Duration.between(start, end).toMillis());
    }
輸出:
子線程在進行計算
Mid拿到Futrue結果對象result:98
task運行結果計算的總和爲:4950
End拿到結果值:3099
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
咱們很顯然能夠發現,mid的時間很短,主線程立刻向下執行了,可是end的時間就比較長了,所以get()拿結果屬於阻塞方法。

FutureTask獲取結果:(結合Callbale使用)
    public static void main(String args[]) {
        Instant start = Instant.now();

        ExecutorService executor = Executors.newCachedThreadPool();
        //使用FutureTask包裝callbale任務,再交給線程池執行
        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
            System.out.println("子線程在進行計算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        });
        //線程池執行任務 這個返回值不須要了,直接就在futureTask對象裏面了
        executor.submit(futureTask);

        Instant mid = Instant.now();
        System.out.println("Mid拿到futureTask結果對象result:" + Duration.between(start, mid).toMillis());

        try {
            System.out.println("task運行結果計算的總和爲:" + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        executor.shutdown();

        Instant end = Instant.now();
        System.out.println("End拿到結果值:" + Duration.between(start, end).toMillis());
    }
輸出:
子線程在進行計算
Mid拿到futureTask結果對象result:86
task運行結果計算的總和爲:4950
End拿到結果值:3088
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
第二種方式(只是進行了簡單包裝而已):

//第二種方式,注意這種方式和第一種方式效果是相似的,只不過一個使用的是ExecutorService,一個使用的是Thread
        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
            System.out.println("子線程在進行計算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        });
        Thread thread = new Thread(futureTask);
        thread.start();
1
2
3
4
5
6
7
8
9
10
11
若是爲了可取消性而使用 Future 但又不提供可用的結果,則能夠聲明 Future<?> 形式類型、並返回 null 做爲底層任務的結果。

Callable和Futrue的區別:Callable用於產生結果,Future用於獲取結果

相關文章
相關標籤/搜索