一队询、Future模式介紹
??JDK1.5 引入了Future模式,F(xiàn)uture模式主要解決在多線程環(huán)境中并發(fā)問題蚌斩。例如:我們在某寶上買了件商品,該商品需要三天的運(yùn)輸才能到你的手上送膳。在這三天時間中你可以做別的事,而不是一直等待商品的到來叠聋。
二、Future接口解讀
??本文的目的是為了讓我們了解Future接口便于進(jìn)行異步編程闻书,從而達(dá)到理論結(jié)合實(shí)踐的目的,所以在本文只能知其然而不能知其所以然魄眉。
??在Java JUC包中的FutureTask類已經(jīng)實(shí)現(xiàn)了Future模式闷袒,這里貼上FutureTask的實(shí)現(xiàn):
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
??從上面繼承關(guān)系我們可以看出FutureTask可以以Runnable被線程執(zhí)行,又作為Future將線程執(zhí)行的結(jié)果返回囊骤。
接下來我們來分析Future接口:
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
下面解釋該接口中聲明的5個方法:
- cancel():用于取消任務(wù)的執(zhí)行,取消成功返回true宫屠,取消失敗返回false滑蚯。參數(shù)mayInterruptIfRunning代表是否可以取消正在執(zhí)行的任務(wù)浪蹂;
- isCancelled:返回任務(wù)是否被成功取消告材;
- isDone: 返回該任務(wù)是否完成;
- get(): 該方法得到執(zhí)行結(jié)果斥赋,注:該方法會阻塞線程;
- get(long timeout, TimeUnit unit) :該方法也得是得到執(zhí)行結(jié)果滑绒,不一樣的是該方法規(guī)定在規(guī)定時間內(nèi)獲得結(jié)果隘膘,如超時會拋出TimeoutException疑故;
三棘幸、Future實(shí)戰(zhàn)
??一般情況我們將ExecutorService和Future結(jié)合使用,因為要返回Future并且我們還想讓線程執(zhí)行后返回一個結(jié)果误续,所以我們使用ExecutorService.submit(Callable Task),在ExecutorServices 4種線程池中我們選擇可緩存的線程池newCachedThreadPool育瓜。
??上代碼之前先描述下需求:周末碼爸爸要在家做道糖醋排骨的犒勞下自己忙碌的一周,他想了下這道菜的流程:他要先切排骨然后先煮排骨這些都可以交給左手來做躏仇,切配菜可以交給右手來做,等待排骨煮好配菜切好后焰手,他就可以抄菜了(廚藝不精,如有專業(yè)的看到這里請忽略細(xì)節(jié))书妻。需求描述完了,上代碼:
Long start = System.nanoTime();
System.out.println(">>>>>>>>>>>>>>>>>>>>"+(System.nanoTime()-start)/1000000+"mill sec");
//創(chuàng)建線程池
ExecutorService service = Executors.newCachedThreadPool();
//創(chuàng)建切排骨線程
Future<String> leftFuture = service.submit(
()->{
//模擬切肉
Thread.sleep(1000);
String str = Thread.currentThread()+ "cute meat";
//模擬煮排骨
Thread.sleep(2000);
return str+"and boil meal";
});
//創(chuàng)建切菜線程
Future<String> rightFuture = service.submit(
()->{
//模擬切菜
Thread.sleep(1000);
return Thread.currentThread()+ "cute veg";
});
System.out.println(leftFuture.get()+" done");//該操作阻塞當(dāng)前線程
System.out.println(rightFuture.get()+" done");
//等待排骨煮好配菜切好咱就炒菜
System.out.println("糖醋排骨出爐");
System.out.println(">>>>>>>>>>>>>>>>>>>>"+(System.nanoTime()-start)/1000000+"mill sec");
運(yùn)行結(jié)果:
>>>>>>>>>>>>>>>>>>>>0mill sec
Thread[pool-1-thread-1,5,main]cute meatand boil meal done
Thread[pool-1-thread-2,5,main]cute veg done
糖醋排骨出爐
<<<<<<<<<<<<<<<<<<<3043mill sec
??從運(yùn)行結(jié)果看程序共花費(fèi)3043毫秒,證明我們在左手線程運(yùn)行時工猜,右手線程也在運(yùn)行中,在打印出菜做好的語句中沒有加任何判斷條件篷帅,是因為get操作會阻塞當(dāng)前線程,一般我推薦采用get(timeout,timeUnit)這樣可以避免程序無線等待犹褒。