一文帶你掌握J(rèn)ava?Future模式的靈活應(yīng)用
第1章:引言
咱們程序員在日常工作中,肯定都遇到過需要處理耗時(shí)任務(wù)的情況,特別是在Java領(lǐng)域。比如說,小黑要從網(wǎng)絡(luò)上下載數(shù)據(jù),或者要執(zhí)行一個(gè)計(jì)算密集型任務(wù),這些操作都可能需要花費(fèi)一些時(shí)間。在這種場景下,如果小黑還要保持程序的響應(yīng)性,就得用到異步編程。Java中的Future模式,就是處理這類問題的一個(gè)非常棒的工具。
Future模式,簡單來說,就是一種能夠管理異步操作的方式。它可以讓咱們的程序在執(zhí)行一個(gè)耗時(shí)任務(wù)的同時(shí),還能繼續(xù)做其他事情。這不僅能提高應(yīng)用程序的性能,還能改善用戶體驗(yàn)。
第2章:Future模式的基本概念
Future模式究竟是什么呢?在Java中,F(xiàn)uture是一個(gè)接口,它代表了一個(gè)可能還沒有完成的異步計(jì)算的結(jié)果。通過這個(gè)接口,小黑可以在計(jì)算完成之前繼續(xù)做其他事情,然后在需要的時(shí)候獲取計(jì)算結(jié)果。
來看個(gè)簡單的例子吧。假設(shè)小黑需要從網(wǎng)絡(luò)上下載一些數(shù)據(jù),這可能需要一些時(shí)間。使用Future,小黑可以這樣做:
ExecutorService executor = Executors.newCachedThreadPool(); Future<String> futureData = executor.submit(() -> { // 這里是模擬下載數(shù)據(jù)的操作,假設(shè)需要耗時(shí)操作 Thread.sleep(2000); // 模擬耗時(shí) return "下載的數(shù)據(jù)"; // 返回下載的數(shù)據(jù) }); // 這里可以繼續(xù)做其他事情,不必等待數(shù)據(jù)下載完成 // ... // 當(dāng)需要使用數(shù)據(jù)時(shí),可以從Future中獲取 try { String data = futureData.get(); // 這會阻塞直到數(shù)據(jù)下載完成 System.out.println("獲取到的數(shù)據(jù): " + data); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown();
在這個(gè)例子中,submit
方法將下載數(shù)據(jù)的任務(wù)提交給了一個(gè)線程池,這個(gè)任務(wù)就是一個(gè)異步操作。然后,小黑可以繼續(xù)執(zhí)行其他代碼,而不用等待數(shù)據(jù)下載完成。當(dāng)小黑需要使用下載的數(shù)據(jù)時(shí),可以調(diào)用futureData.get()
來獲取。如果數(shù)據(jù)還沒準(zhǔn)備好,這個(gè)調(diào)用會等待,直到數(shù)據(jù)下載完成。
通過這個(gè)例子,咱們可以看到,F(xiàn)uture模式可以讓小黑的程序更加靈活和高效。咱們不僅可以優(yōu)化程序的性能,還能提高用戶體驗(yàn),因?yàn)橛脩舨恍枰却粋€(gè)操作完成才能進(jìn)行下一個(gè)操作。
第3章:Java中的Future接口
Future接口主要用于表示異步計(jì)算的結(jié)果。它提供了幾個(gè)關(guān)鍵的方法來管理這些計(jì)算,最常用的包括get()
和isDone()
。這些方法讓小黑能夠在計(jì)算完成之前或之后進(jìn)行操作。
咱們先看看Future的一些基本用法。想象一下,小黑現(xiàn)在有個(gè)任務(wù)是計(jì)算一系列數(shù)字的總和,這個(gè)計(jì)算可能會花費(fèi)一些時(shí)間。小黑可以使用Future來異步地執(zhí)行這個(gè)任務(wù):
ExecutorService executor = Executors.newCachedThreadPool(); Future<Integer> futureSum = executor.submit(() -> { int sum = 0; for (int i = 1; i <= 10; i++) { sum += i; Thread.sleep(100); // 模擬耗時(shí)的計(jì)算過程 } return sum; // 返回計(jì)算結(jié)果 }); // 這里可以執(zhí)行其他任務(wù)... // ... // 檢查任務(wù)是否完成 if (futureSum.isDone()) { try { System.out.println("計(jì)算結(jié)果: " + futureSum.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } else { System.out.println("計(jì)算還在進(jìn)行中..."); } executor.shutdown();
在這個(gè)例子中,submit
方法將計(jì)算任務(wù)提交給了一個(gè)線程池。這個(gè)任務(wù)會異步執(zhí)行,小黑可以在等待結(jié)果的同時(shí)做其他事情。使用isDone()
方法可以檢查計(jì)算是否已完成。如果完成了,就可以使用get()
方法獲取結(jié)果。
但注意,get()
方法是阻塞的,也就是說,如果計(jì)算還沒完成,它會讓當(dāng)前線程等待。這可能不是小黑想要的,特別是在圖形用戶界面(GUI)編程中,這樣可能會導(dǎo)致界面凍結(jié)。所以,小黑在使用get()
方法時(shí),需要小心考慮。
還有一個(gè)點(diǎn),就是異常處理。如果異步任務(wù)中發(fā)生了異常,它會被封裝在一個(gè)ExecutionException
中。當(dāng)小黑調(diào)用get()
方法時(shí),這個(gè)異常會被拋出。所以,小黑在處理結(jié)果時(shí),也要做好異常處理。
咱們可以看出,F(xiàn)uture接口提供了一種非常靈活的方式來處理異步任務(wù)。小黑可以利用這些方法,優(yōu)化程序的性能,同時(shí)提高代碼的可讀性和可維護(hù)性。
第4章:Future的高級應(yīng)用
組合異步任務(wù)
在實(shí)際開發(fā)中,經(jīng)常會遇到需要順序執(zhí)行多個(gè)異步任務(wù)的情況。比如,小黑先下載數(shù)據(jù),然后處理這些數(shù)據(jù)。這時(shí),咱們可以通過Future將這些任務(wù)串聯(lián)起來。來看一個(gè)例子:
ExecutorService executor = Executors.newCachedThreadPool(); // 第一個(gè)異步任務(wù):下載數(shù)據(jù) Future<String> futureData = executor.submit(() -> { // 模擬下載數(shù)據(jù)的操作 Thread.sleep(2000); return "下載的數(shù)據(jù)"; }); // 第二個(gè)異步任務(wù):處理數(shù)據(jù) Future<String> futureProcessed = executor.submit(() -> { try { // 等待并獲取第一個(gè)任務(wù)的結(jié)果 String data = futureData.get(); // 模擬數(shù)據(jù)處理過程 return "處理后的" + data; } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); return "處理過程中出錯(cuò)"; } }); try { // 獲取最終處理后的數(shù)據(jù) System.out.println("最終結(jié)果: " + futureProcessed.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown();
在這個(gè)例子中,小黑首先提交了一個(gè)下載數(shù)據(jù)的異步任務(wù),然后提交了一個(gè)處理數(shù)據(jù)的異步任務(wù)。第二個(gè)任務(wù)中,通過futureData.get()
等待并獲取第一個(gè)任務(wù)的結(jié)果。這樣,這兩個(gè)任務(wù)就被順利串聯(lián)起來了。
處理異常
處理異步任務(wù)時(shí),異常管理也非常重要。如果任務(wù)執(zhí)行過程中出現(xiàn)異常,F(xiàn)uture會把這個(gè)異常包裝成ExecutionException
。咱們需要妥善處理這些異常,以避免程序崩潰。例如:
Future<Integer> futureTask = executor.submit(() -> { if (new Random().nextBoolean()) { throw new RuntimeException("出錯(cuò)啦!"); } return 42; }); try { Integer result = futureTask.get(); System.out.println("任務(wù)結(jié)果: " + result); } catch (ExecutionException e) { System.out.println("任務(wù)執(zhí)行過程中出現(xiàn)異常: " + e.getCause().getMessage()); } catch (InterruptedException e) { e.printStackTrace(); } executor.shutdown();
這里,小黑提交了一個(gè)可能會拋出異常的任務(wù)。通過捕獲ExecutionException
,咱們可以得知任務(wù)執(zhí)行過程中是否出現(xiàn)了異常,并相應(yīng)地處理。
第5章:與Future相關(guān)的工具類
ExecutorService:管理線程池
ExecutorService是一個(gè)管理線程池的工具類。它可以讓小黑更方便地執(zhí)行異步任務(wù),而不需要手動創(chuàng)建和管理線程。比如,小黑可以使用ExecutorService來提交Callable任務(wù):
ExecutorService executorService = Executors.newFixedThreadPool(10); // 創(chuàng)建一個(gè)固定大小的線程池 Future<String> future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { // 模擬耗時(shí)操作 Thread.sleep(2000); return "任務(wù)結(jié)果"; } }); try { // 獲取異步任務(wù)的結(jié)果 String result = future.get(); System.out.println("異步任務(wù)的結(jié)果是:" + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executorService.shutdown(); // 關(guān)閉線程池
在這個(gè)例子中,小黑創(chuàng)建了一個(gè)固定大小的線程池,然后提交了一個(gè)Callable任務(wù)。Callable是一個(gè)返回結(jié)果的任務(wù),和Runnable稍有不同。使用ExecutorService可以讓小黑更加專注于任務(wù)的邏輯,而不是線程的管理。
使用ScheduledExecutorService進(jìn)行定時(shí)任務(wù)
如果小黑想要執(zhí)行一些定時(shí)或周期性的任務(wù),那么ScheduledExecutorService是一個(gè)非常好的選擇。它可以讓小黑安排在未來某個(gè)時(shí)間點(diǎn)執(zhí)行任務(wù),或者周期性地執(zhí)行任務(wù)。
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5); // 延遲3秒執(zhí)行任務(wù) ScheduledFuture<?> scheduledFuture = scheduledExecutor.schedule(new Callable<Object>() { @Override public Object call() throws Exception { System.out.println("延遲執(zhí)行的任務(wù)"); return null; } }, 3, TimeUnit.SECONDS); // 定期執(zhí)行任務(wù),每2秒執(zhí)行一次 scheduledExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("定期執(zhí)行的任務(wù)"); } }, 0, 2, TimeUnit.SECONDS); // 記得關(guān)閉scheduledExecutor
在這個(gè)例子中,小黑使用ScheduledExecutorService安排了兩個(gè)任務(wù):一個(gè)是延遲3秒執(zhí)行的任務(wù),另一個(gè)是每2秒執(zhí)行一次的任務(wù)。這對于需要定時(shí)執(zhí)行任務(wù)的場景非常有用。
第6章:Java中的其他異步模式
CompletableFuture:更強(qiáng)大的異步編程工具
Java 8引入了CompletableFuture
,它是Future的增強(qiáng)版,提供了更豐富的API,使得異步編程更加靈活。CompletableFuture支持函數(shù)式編程風(fēng)格,可以輕松地組合和鏈?zhǔn)秸{(diào)用異步操作。
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { // 異步執(zhí)行任務(wù) try { Thread.sleep(2000); } catch (InterruptedException e) { throw new IllegalStateException(e); } return "異步計(jì)算的結(jié)果"; }); // 組合操作,對結(jié)果進(jìn)行轉(zhuǎn)換 CompletableFuture<String> future = completableFuture.thenApply(result -> "處理過的" + result); // 獲取最終結(jié)果 try { System.out.println(future.get()); // 輸出:處理過的異步計(jì)算的結(jié)果 } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
在這個(gè)例子中,supplyAsync
方法用來異步執(zhí)行任務(wù),thenApply
方法則用來處理這個(gè)任務(wù)的結(jié)果。這種鏈?zhǔn)秸{(diào)用的方式,使得異步編程變得非常簡潔和直觀。
RxJava:響應(yīng)式編程
RxJava是另一種流行的異步編程框架。它基于觀察者模式,提供了豐富的操作符來處理異步數(shù)據(jù)流。RxJava特別適合處理復(fù)雜的事件驅(qū)動程序,比如GUI應(yīng)用或者網(wǎng)絡(luò)編程。
Observable<String> observable = Observable.create(emitter -> { new Thread(() -> { try { Thread.sleep(2000); emitter.onNext("RxJava的異步數(shù)據(jù)"); emitter.onComplete(); } catch (InterruptedException e) { emitter.onError(e); } }).start(); }); observable.subscribe( item -> System.out.println(item), // 處理數(shù)據(jù) error -> error.printStackTrace(), // 處理錯(cuò)誤 () -> System.out.println("完成") // 處理完成 );
在這個(gè)例子中,小黑使用Observable
創(chuàng)建了一個(gè)異步數(shù)據(jù)流,然后通過subscribe
方法來處理這個(gè)數(shù)據(jù)流。RxJava的強(qiáng)大之處在于它提供了大量的操作符,可以輕松地對數(shù)據(jù)流進(jìn)行過濾、轉(zhuǎn)換、組合等操作。
選擇合適的異步模式
Future、CompletableFuture和RxJava都是Java中處理異步編程的有效工具。選擇哪一個(gè)主要取決于具體的應(yīng)用場景和個(gè)人的編程風(fēng)格。如果小黑需要簡單的異步任務(wù)管理,F(xiàn)uture就足夠了;如果需要更靈活的鏈?zhǔn)秸{(diào)用和函數(shù)式編程特性,CompletableFuture是一個(gè)好選擇;如果要處理復(fù)雜的數(shù)據(jù)流和事件驅(qū)動編程,RxJava可能更合適。
第7章:Future的局限性和解決方案
1. 阻塞問題
Future的一個(gè)主要問題是,當(dāng)調(diào)用get()
方法時(shí),如果任務(wù)還沒有完成,就會阻塞當(dāng)前線程。這在某些情況下會導(dǎo)致性能問題,特別是在處理大量并行任務(wù)時(shí)。
解決方案:
- 使用
isDone()
方法檢查任務(wù)是否完成,以避免阻塞。 - 使用
CompletableFuture
,它提供了非阻塞的thenApply
、thenAccept
等方法,可以在任務(wù)完成時(shí)觸發(fā)回調(diào)。
2. 異常處理
Future在異常處理方面不夠靈活。如果異步任務(wù)執(zhí)行過程中發(fā)生異常,這個(gè)異常會被封裝在ExecutionException
中,只有在調(diào)用get()
方法時(shí)才能被捕獲。
解決方案:
使用CompletableFuture
的exceptionally
方法來處理異常。這允許小黑在鏈?zhǔn)秸{(diào)用中優(yōu)雅地處理異常。
3. 任務(wù)組合的復(fù)雜性
使用Future進(jìn)行復(fù)雜的任務(wù)組合和流程控制比較困難,特別是當(dāng)涉及到多個(gè)異步計(jì)算結(jié)果之間的依賴時(shí)。
解決方案:
利用CompletableFuture
的組合方法,如thenCompose
和thenCombine
,可以更加容易地實(shí)現(xiàn)復(fù)雜的任務(wù)組合和流程控制。
4. 無法直接取消任務(wù)
Future提供了cancel
方法來嘗試取消任務(wù),但這種取消并不總是有效的。如果任務(wù)已經(jīng)開始執(zhí)行,那么它將無法被取消。
解決方案:
- 使用
CompletableFuture
,它提供了更靈活的取消機(jī)制。 - 設(shè)計(jì)異步任務(wù)時(shí),增加檢查中斷狀態(tài)的邏輯,使得任務(wù)能夠響應(yīng)中斷請求。
第8章:總結(jié)
Future模式是Java異步編程的基礎(chǔ),它允許咱們將耗時(shí)的任務(wù)放在后臺執(zhí)行,提高了程序的性能和響應(yīng)性。
盡管Future有一些局限性,如阻塞問題和異常處理不夠靈活,但咱們可以通過使用CompletableFuture
或結(jié)合其他異步編程技術(shù)來克服這些限制。
Java中還有其他異步編程的工具和框架,如RxJava、ScheduledExecutorService等,它們在特定場景下可以提供更優(yōu)的解決方案。
到此這篇關(guān)于一文帶你掌握J(rèn)ava Future模式的靈活應(yīng)用的文章就介紹到這了,更多相關(guān)Java Future模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring MVC如何測試Controller(使用springmvc mock測試)
這篇文章主要介紹了詳解Spring MVC如何測試Controller(使用springmvc mock測試),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12解決OpenFeign遠(yuǎn)程調(diào)用返回的對象總是null問題
OpenFeign在SpringCloud中用于遠(yuǎn)程調(diào)用,配置簡單,在使用Ribbon或Hystrix時(shí),需要注意path參數(shù)必須以/開頭,否則回參會是null2024-11-11Java實(shí)現(xiàn)升級版布谷鳥闖關(guān)游戲的示例代碼
升級版布谷鳥闖關(guān)游戲是一個(gè)基于java的布谷鳥闖關(guān)游戲,鼠標(biāo)左鍵點(diǎn)擊控制鳥的位置穿過管道間的縫隙。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-02-02java中@JSONField和@JsonProperty注解的使用說明及對比
@JSONField與@JsonProperty隸屬兩個(gè)不同的包,前者是阿里系的fastjson包,后者是spring?boot官方使用的jackson包,本文主要介紹了java中@JSONField和@JsonProperty注解的使用說明及對比,感興趣的可以了解一下2023-11-11java實(shí)現(xiàn)批量導(dǎo)入.csv文件到mysql數(shù)據(jù)庫
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)批量導(dǎo)入.csv文件到mysql數(shù)據(jù)庫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Java利用移位運(yùn)算將int型分解成四個(gè)byte型的方法
今天小編就為大家分享一篇關(guān)于Java利用移位運(yùn)算將int型分解成四個(gè)byte型的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12Spring?Boot教程之提高開發(fā)效率必備工具lombok
這篇文章主要介紹了Spring?Boot教程之提高開發(fā)效率必備工具lombok的相關(guān)資料,需要的朋友可以參考下2022-08-08Java虛擬機(jī)如何運(yùn)行Java字節(jié)碼
這篇文章主要介紹了Java虛擬機(jī)如何運(yùn)行Java字節(jié)碼的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06