一 、使用方式一般爲兩種:
1 . 創建線程執行FutureTask並通過FutureTask得到異步結果;
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
// 1.創建任務,這個FutureTask可以是Callable的也可以是Runnable的
FutureTask task = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(3000); // 模擬異步任務執行
return "我是異步結果";
}
});
// 2.創建線程執行任務
Thread thread = new Thread(task);
thread.start();
// 3.主線程模擬在異步任務之間執行其他任務
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 4.得到異步任務結果
System.out.println(task.get());
System.out.println("運行時間 : " + (System.currentTimeMillis() - start) + "ms");
}
運行結果:
我是異步結果
運行時間 : 6002msProcess finished with exit code 0
2 . 使用線程池管理線程通過Future接口得到結果;
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future future = executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(3000);
return "我是異步結果";
}
});
System.out.println("主線程工作");
Thread.sleep(6000);
System.out.println(future.get());
System.out.println("運行時間 : " + (System.currentTimeMillis() - start) + "ms");
}
運行結果:
主線程工作
我是異步結果
運行時間 : 6012ms
注意:
異步結果用Future或者FutureTask都可以獲取。Future是接口,FutureTask是實現了這個接口的實現類。FutureTask除了實現Future接口外,還實現了Runnable接口。所以不論是Callable任務還是Runnable任務都可以用FutureTask完成,而這兩種都會最終都會調用Callable的call方法,對於Runnable實現調用call的方式是使用了適配器模式。
(圖片來源網上,侵刪)
debug第二個程序,我們可以看到這時定義的Future也是FutureTask實現的。
二、異步原理
1. 異步模型:
可以總結爲生產者和消費者,生產者就是線程處理任務,消費者就是調用者得到任務結果。那生產者消費者模型還有緩衝區啊,這裏面就是用FutureTask中的outcome字段在任務結束後緩存結果。
2 . 異步流程:
主線程main開啓線程a執行任務,並馬上返回Future,如果是剛新建任務,狀態置爲new。當main線程想取線程a執行的結果(調用了FutureTask的get方法)時,就查看這個任務的state是否執行完成(completing),如果還沒有完成就阻塞當前線程(main)等待線程a執行完成。