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
實現了 Runnable
和 Future
接口,由於實現了 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
——f1
和 f2
,f1
完成洗水壺
、燒開水
、泡茶
的任務,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
如有不對的地方,還請指正,博主會及時更改!
如果幫到你了,還請點個贊哈!