java 深入剖析FutureTask

FutureTask介紹

首先來看下FutureTask的關係圖,瞭解下是什麼

類關係圖在IDEA中的快捷鍵爲Ctrl+Alt+U

在這裏插入圖片描述
由上圖可以看出FutureTask實現了RunnableFuture接口,RunnableFuture接口又繼承了Future接口和Runnable接口(Runnable函數式接口)

接下來看下Future接口的方法

  • 取消任務:boolean cancel(boolean mayInterruptIfRunning);

  • 判斷任務是否取消:boolean isCancelled();

  • 判斷任務是否結束boolean isDone();

  • 獲取任務返回值V get() throws InterruptedException, ExecutionException;

  • 獲取任務返回值(支持超時)V get(long timeout, TimeUnit unit) throws InterruptedException,ExecutionException, TimeoutException;

再來看下Runnable接口的方法

public abstract void run();

最後來看下FutureTask接口構造方法

  • FutureTask(Callable<V> callable)
  • FutureTask(Runnable runnable, V result)

如何使用 FutureTask

其實很簡單,FutureTask 實現了 RunnableFuture 接口,由於實現了 Runnable 接口,所以可以將 FutureTask 對象作爲任務提交給 ThreadPoolExecutor 去執行,也可以直接被 Thread 執行;又因爲實現了 Future 接口,所以也能用來獲得任務的執行結果。

爲什麼可以將 FutureTask 對象作爲任務提交給 ThreadPoolExecutor 去執行呢?
在這裏插入圖片描述
可以看到ThreadPoolExecutor是ExecutorService的實現類,最終會將FutureTask提交給ThreadPoolExecutor

爲什麼可以直接被Thread執行呢?

Thread源碼如下:

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

會將FutureTask任務提交給target對象

下面的示例代碼是將 FutureTask 對象提交給 ThreadPoolExecutor 去執行。


import java.util.concurrent.*;

public class FutureTaskDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創建任務
        FutureTask<Integer> futureTask = new FutureTask<>(() -> 1 + 2);
        //創建線程池
        ExecutorService executor = Executors.newFixedThreadPool(1);
        //執行任務
        executor.submit(futureTask);
        //獲取任務的執行結果
        Integer integer = futureTask.get();
        System.out.println(integer); //結果爲3

    }
}

FutureTask 對象直接被 Thread 執行的示例代碼如下所示。相信你已經發現了,利用 FutureTask 對象可以很容易獲取子線程的執行結果

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class FutureTaskDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創建任務
        FutureTask<Integer> futureTask = new FutureTask<>(() -> 1 + 2);
        //創建線程,將任務提交給target
        Thread t = new Thread(futureTask);
        //啓動線程
        t.start();
        //獲取任務的執行結果
        Integer integer = futureTask.get();
        System.out.println(integer); //結果爲3
    }
}

“燒水泡茶”程序

假設線程T1執行洗水壺燒開水泡茶線程T2執行洗茶壺洗茶杯拿茶葉。需要注意的是線程T1的泡茶任務需要等線程T2拿到茶葉後才能執行泡茶操作。
在這裏插入圖片描述
首先,我們創建了兩個 FutureTask——f1f2f1 完成洗水壺燒開水泡茶的任務,f2 完成洗茶壺洗茶杯拿茶葉的任務;這裏需要注意的是 f1 這個任務在執行泡茶任務前,需要等待 f2 把茶葉拿來,所以 f1 內部需要引用 f2,並在執行泡茶之前,調用 f2 的 get() 方法實現等待。

import java.util.concurrent.*;


public class FutureTaskDemo3 {

    /**
     * 兩條線程:
     * T1:洗水壺、燒開水、泡茶
     * T2:洗茶壺、洗茶杯、拿茶葉
     *
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創建T2的任務f2
        FutureTask<String> f2 = new FutureTask<>(new T2Task());
        //創建T1的任務f1
        FutureTask<String> f1 = new FutureTask<>(new T1Task(f2));
        //創建線程T1,執行f1任務
        Thread T1 = new Thread(f1);
        //創建線程T2,執行f2任務
        Thread T2 = new Thread(f2);
        T1.start();
        T2.start();
        System.out.println(f1.get());
        System.out.println("終於開始喝茶了");

    }


    static class T1Task implements Callable<String> {
        FutureTask<String> futureTask;

        T1Task(FutureTask<String> t) {
            this.futureTask = t;
        }

        @Override
        public String call() throws Exception {
            //洗水壺
            System.out.println("洗水壺。。。");
            Thread.sleep(1000);
            //燒開水
            System.out.println("燒開水。。。");
            Thread.sleep(15000);
            System.out.println("線程T1正在獲取線程T2拿到的茶葉");
            //獲取T2線程的茶葉
            String s = futureTask.get();
            //泡茶
            return "上茶:" + s;
        }
    }


    static class T2Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            //洗茶壺
            System.out.println("洗茶壺。。。");
            Thread.sleep(1000);
            //洗茶杯
            System.out.println("洗茶杯。。。");
            Thread.sleep(2000);
            //拿茶葉
            System.out.println("拿茶葉。。。");
            Thread.sleep(1000);
            return "龍井";
        }
    }


}

結果
在這裏插入圖片描述

開心一刻

一學生上課時老想打球,眼睛不住地往操場上盯,老師批語他說:“你呀,人在教室,心在操場,這怎麼行呢?”學生聽了說道 :“老師,讓我人去操場,把心留在教室,好嗎?”

在這裏插入圖片描述
詳細請看王寶令老師:https://time.geekbang.org/column/article/91292

如有不對的地方,還請指正,博主會及時更改!
如果幫到你了,還請點個贊哈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章