Java 1.5開始, 提供了 Callable
和 Future
, 通過它們可以在任務執行完畢之後得到任務執行結果.
當需要調用幾個執行很慢的方法時, 可以使用多線程一起執行這幾個方法, 等所有方法執行完畢後得到執行結果, 在進行別的處理.
Future 的主要方法
Future
接口主要包括 5 個方法:
get()
方法可以當任務結束後返回一個結果, 如果調用時, 工作還沒有結束, 則會阻塞線程, 直到任務執行完畢.
get(long timeout,TimeUnit unit)
做多等待 timeout
的時間就會返回結果.
cancel(boolean mayInterruptIfRunning)
方法可以用來停止一個任務.
isDone()
方法判斷當前方法是否完成.
isCancel()
方法判斷當前方法是否取消.
Future 示例 demo
需求場景: 等早餐過程中, 包子需要 3 秒, 涼菜需要 1 秒, 普通的多線程需要四秒才能完成. 先等涼菜, 再等包子, 因爲等涼菜時, 普通多線程啓動 start()
方法, 執行 run()
中具體方法時, 沒有返回結果, 所以如果要等有返回結果, 必須是要1秒結束後才知道結果.
public static void main(String[] args) throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
// 等涼菜
Callable ca1 = new Callable() {
@Override
public String call() throws Exception {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "涼菜準備完畢";
}
};
FutureTask<String> ft1 = new FutureTask<String>(ca1);
new Thread(ft1).start();
// 等包子 -- 必須要等待返回的結果,所以要調用join方法
Callable ca2 = new Callable() {
@Override
public Object call() throws Exception {
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "包子準備完畢";
}
};
FutureTask<String> ft2 = new FutureTask<String>(ca2);
new Thread(ft2).start();
System.out.println(ft1.get());
System.out.println(ft2.get());
long end = System.currentTimeMillis();
System.out.println("準備完畢時間:" + (end - start));
}
還有一個比較典型的例子就是設置超時時間:
//固定大小的線程池,同時只能接受5個任務
static ExecutorService mExecutor = Executors.newFixedThreadPool(5);
final static long timeout = 4 ;
/**
* 模擬在預定時間內獲取廣告信息
* @throws InterruptedException
*/
static void rederPageWithAd(final String pageTitle) throws InterruptedException{
Future<String> f = mExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("開始加載廣告信息");
int randomTime = new Random().nextInt(5) + 1;//限制耗時不會出現0s,不會大於10s
Thread.sleep(100 * 1000);
System.out.println("正常加載廣告耗時:" + randomTime +"s");
return pageTitle;
}
});
String page;
try {
//在預計時間內等待
System.out.println("預期任務執行完時間:" + timeout + "s");
//page = f.get();
page = f.get(timeout, TimeUnit.SECONDS);
} catch (ExecutionException e) {
page = "出現執行異常,顯示默認的廣告頁面";
} catch (TimeoutException e) {
page = "任務執行超時,顯示默認的廣告頁面";
f.cancel(true);//取消沒有執行完的任務,設置爲ture說明任務能被中斷,否則執行中的任務要完成
}
System.out.println("成功加載廣告頁面:" + page);
}
public static void main(String[] args) {
try {
List<String> titleList = new ArrayList<String>();
titleList.add("體育賽事");
titleList.add("娛樂新聞");
titleList.add("實時聚焦");
titleList.add("國際諮詢");
titleList.add("影視天下");
titleList.add("遊戲風雲");
for (String string : titleList) {
rederPageWithAd(string);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
/**
* 只有執行了shutdown方法,執行isTerminated纔有效。否則isTerminated一直爲ture
*/
mExecutor.shutdown();
while(true){
if(mExecutor.isTerminated()){
System.out.println("所有任務都執行完了,關閉線程池");
break;
}
}
}
}
值得注意的是: 當主線程調用 Future
的 get
方法的時候會獲取到從線程中返回的結果數據. 如果在線程的執行過程中發生了異常, get
會獲取到異常的信息.