Future到底是什么東西?很多人都對這個東西感到特別奇怪(好吧,我承認,那個很多人就只是我自己而已),就我現在的理解,因為本人在並發這方面沒有多少實踐經驗,所以只好就着一些資料和自己的理解給它下個定義,Future就是保存我們任務的完成信息,比如說,任務中會通過返回某些東西告訴別人它已經結束了,而Future中就保存了這種信息。利用Futu保存和得到任務的結果的用法如下:
Future<String> future = threadPool.submit(new Callable<String>(){ @Override public String call() throws Exception{ Thread.sleep(3000); return "future"; } try{ System.out.println("waiting....."); System.out.println(future.get()); }catch(InterruptedException e){ e.printStackTrace(); }catch(ExecutionException e){ e.printStackTrace(); }
注意到沒?代碼中的System.out.println(future.get())這一句,就是返回任務的結果,而任務的結果是保存在Future<String>中的,相信大家都有注意到,
Future<String> future = threadPool.submit(new Callable<String>(){ @Override public String call() throws Exception{} }
Callable相當於Runnable,所以,這里實現的是一個線程,但是與Runnable不同的是,它是具有返回值的,這個返回值就是我們想要任務返回的結果,比如說,我們想要任務返回的是一個提示信息,那么,返回值可以是String,然后在我們要實現的call()方法中return一句提示信息,接着只要使用Future類的get()方法,就可以從里面得到提示信息了,只要任務完成。所以,由此我們可以知道,java SE5比起以前來,在並發這方面做了更多的工作,它完善了我們的並發線程機制,使我們可以更好的根據任務的完成情況來進行與其他任務的協作,比如說,我們可以通過Future的返回值來決定是否終止任務,或者開啟另一個任務。任務的終止可以使用Future的方法future.cancel(boolean),其中boolean為true或false,來決定是否終止,至於開啟另一個任務,可以重新開啟另一個線程,但是這里就馬上有個問題浮現出來,就是當Futrue的結果返回來時,該任務有沒有結束呢?因為這時一定已經執行完該任務的call()方法。是的,該任務已經結束了,只是我們沒有取出它的返回結果而已。
看到上面,相信大家一定都對Future的新特性產生非常濃厚的興趣,非常想要將這個新玩意兒馬上運用起來,但是,且慢,每次在遇到這種新東西的時候,我們都會有一個念頭,那就是我們有必要使用嗎?如果舊的東西已經足夠用了,那為什么還要用多余的方法呢?是的,這種想法是對的,因為我們的程序設計原則都是能夠盡量簡單則盡量簡單,但是Future是一個接口,一個泛型接口,適合各種返回值的情況,而且這個接口提供了很多有用的方法,再加上,我們永遠無法知道我們的代碼以后到底會變成怎么樣子,是否需要添加新的功能等等,而這些,如果一開始使用的是舊的東西的話,添加新的東西,那么,我們就要對我們的代碼進行修改,但是,我是這么認為的,就目前而言,Thread修改為Future並不是很難,所以,這方面倒是沒有多大顧慮,熟悉啥就用啥,至少都要了解,因為我們在寫代碼時,更多時間里是在閱讀別人的代碼,如果別人使用的代碼是使用以前的接口的話,而且這種情況是非常常見的,所以,我們必須要看得懂代碼並且能夠將其轉化為我們的新接口,這些就需要我們能夠對其有一定的了解,並且明白它們之間的聯系和區別。所以,接下來就是介紹一下新接口的一些方法以便我們能夠更好的使用新接口。
ExecutorService executor = Executors.newSingleThreadExecutor(); FutureTask<String> future = new FutureTask<String>(new Callable<String>() {//使用Callable接口作為構造參數 public String call() { //真正的任務在這里執行,這里的返回值類型為String,可以為任意類型 }}); executor.execute(future); //在這里可以做別的任何事情 try { result = future.get(5000, TimeUnit.MILLISECONDS); //取得結果,同時設置超時執行時間為5秒。同樣可以用future.get(),不設置執行超時時間取得結果 } catch (InterruptedException e) { futureTask.cancel(true); } catch (ExecutionException e) { futureTask.cancel(true); } catch (TimeoutException e) { futureTask.cancel(true); } finally { executor.shutdown(); }
這里就是FutureTask的一般用法,它最大的好處就是我們可以將任務交給執行器后執行其他操作,然后再從里面得到任務的結果。這里必須要注意,只有FutureTask這種既實現Runnable又實現Callable才能夠通過executor()遞交給ExecutorService,而Future不行,只能通過submit(),因為executor()要求的參數是一個實現了Runnable的類。如果我們不想要直接構造Future對象,那么我們可以這樣寫:
ExecutorService executor = Executors.newSingleThreadExecutor(); FutureTask<String> future = executor.submit( new Callable<String>() {//使用Callable接口作為構造參數 public String call() { //真正的任務在這里執行,這里的返回值類型為String,可以為任意類型 }}); //在這里可以做別的任何事情 //同上面取得結果的代碼
這里是使用ExecutorService.submit方法來獲得Future對象,submit方法既支持Callable接口類型,也支持Runnable接口作為參數,具有很大的靈活性,而且所有的submit()方法都會返回一個Future值,無論是Runnable還是Callable。上面兩種方法哪種比較好?其實都一樣,只是第二種的話,可以在定義FutureTask的同時就將FutureTask提交給Executor。個人的話,比較傾向於第二種,因為我們的代碼如果在不影響閱讀性的基礎上能夠越簡單越好,哪怕是一句代碼。
for (;;) { schedule = getNextScheduledTaskTime(); while(schedule > now()) { try { data = subscription.waitNext(schedule - now()); processData(data); } catch(Exception e) {...} } doScheduledTask(); }
