91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解異步編程的Future

發布時間:2021-10-29 10:18:33 來源:億速云 閱讀:124 作者:iii 欄目:web開發

本篇內容介紹了“如何理解異步編程的Future”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

先聊聊線程池的提交方式

談到 Future 的時候,我們基本上就會想到線程池,想到它的幾種提交方式。

先是最簡單的,execute 方式提交,不關心返回值的,直接往線程池里面扔任務就完事:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         //execute(Runnable command)方法。沒有返回值         executor.execute(() -> {             System.out.println("關注why技術");         });         Thread.currentThread().join();     } }

可以看一下 execute 方法,接受一個 Runnable 方法,返回類型是 void:

如何理解異步編程的Future

然后是 submit 方法。你知道線程池有幾種 submit 方法嗎?

雖然你經常用,但是可能你從來沒有關心過人家。呸,渣男:

如何理解異步編程的Future

有三種 submit。這三種按照提交任務的類型來算分為兩個類型。

  • 提交執行 Runnable 類型的任務。

  • 提交執行 Callable 類型的任務。

但是返回值都是 Future,這才是我們關心的東西。

也許你知道線程池有三種 submit 方法,但是也許你根本不知道里面的任務分為兩種類型,你就只知道往線程池里面扔,也不管扔的是什么類型的任務。

我們先看一下 Callable 類型的任務是怎么執行的:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         Future<String> future = executor.submit(() -> {             System.out.println("關注why技術");             return "這次一定!";         });         System.out.println("future的內容:" + future.get());         Thread.currentThread().join();     } }

這里利用 lambda 表達式,直接在任務體里面帶上一個返回值,這時你看調用的方法就變成了這個:

如何理解異步編程的Future

運行結果也能拿到任務體里面的返回了。輸出結果如下:

如何理解異步編程的Future

好,接下來再說說 submit 的任務為 Runable 類型的情況。

這個時候有兩個重載的形式:

如何理解異步編程的Future

標號為 ① 的方法扔進去一個 Runable 的任務,返回一個 Future,而這個返回的 Future  ,相當于是返回了一個寂寞。下面我會說到原因。

標號為 ② 的方法扔進去一個 Runable 的任務的同時,再扔進去一個泛型 T ,而巧好返回的 Future 里面的泛型也是  T,那么我們大膽的猜測一下這就是同一個對象。如果是同一個對象,說明我們可以一個對象傳到任務體里面去一頓操作,然后通過 Future  再次拿到這個對象的。一會就去驗證。

來,先驗證標號為 ① 的方法,我為啥說它返回了一個寂寞。

首先,還是先把測試案例放在這里:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         Future<?> future = executor.submit(() -> {             System.out.println("關注why技術");         });         System.out.println("future的內容:" + future.get());         Thread.currentThread().join();     } }

可以看到,確實是調用的標號為 ① 的方法:

如何理解異步編程的Future

同時,我們也可以看到 future.get() 方法的返回值為 null。

你說,這不是返回了一個寂寞是干啥?

當你想用標號為 ① 的方法時,我勸你直接用 execute 方式提交任務。還不需要構建一個寂寞的返回值,徒增無用對象。

接下來,我們看看標號為 ② 的方法是怎么用的:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));         AtomicInteger atomicInteger = new AtomicInteger();         Future<AtomicInteger> future = executor.submit(() -> {             System.out.println("關注why技術");             //在這里進行計算邏輯             atomicInteger.set(5201314);         }, atomicInteger);          System.out.println("future的內容:" + future.get());         Thread.currentThread().join();     } }

可以看到改造之后,確實是調用了標號為 ② 的方法:

如何理解異步編程的Future

future.get() 方法的輸出值也是異步任務中我們經過計算后得出的 5201314。

你看,渣男就是這樣,明明不懂你,還非得用甜言蜜語來轟炸你。呸。

好了。綜上,線程池的提交方式一共有四種:一種 execute,無返回值。三種 submit,有返回值。

submit 中按照提交任務的類型又分為兩種:一個是 Callable,一個是 Runable。

submit 中 Runable 的任務類型又有兩個重載方法:一個返回了個寂寞,一個返回了個渣男。哦,不。一個返回了個寂寞,一個返回了個對象。

這個時候就有人要站出來說:你說的不對,你就是瞎說,明明就只有 execute 這一種提交方式。

是的,“只有 execute 這一種提交方式”這一種說法也是沒錯的。

請看源碼:

如何理解異步編程的Future

三種 submit 方法里面調用的都是 execute 方法。

能把前面這些方法娓娓道來,從表面談到內在的這種人,才是好人。

如何理解異步編程的Future

只有愛你,才會把你研究透。

當然,還有這幾種提交方式,用的不多,就不展開說了:

如何理解異步編程的Future

好,上面這些東西捋清楚了之后。我們再聚焦到返回值 Future 上:

從上面的代碼我們可以看出,當我們想要返回值的時候,都需要調用下面的這個 get() 方法:

如何理解異步編程的Future

而從這個方法的描述可以看出,這是一個阻塞方法。拿不到值就在那里等著。當然,還有一個帶超時時間的 get 方法,等指定時間后就不等了。

呸,渣男。沒耐心,這點時間都舍不得等。

總之就是有可能要等的。只要等,那么就是阻塞。只要是阻塞,就是一個假異步。

所以總結一下這種場景下返回的 Future 的不足之處:

  • 只有主動調用 get 方法去獲取值,但是有可能值還沒準備好,就阻塞等待。

  • 任務處理過程中出現異常會把異常隱藏,封裝到 Future 里面去,只有調用 get 方法的時候才知道異常了。

寫到這里的時候我不禁想起一個形象的例子,我給你舉一個。

假設你想約你的女神一起去吃飯。女神嘛,肯定是要先畫個美美的妝才會出去逛街的。而女神化妝就可以類比為我們提交的一個異步任務。

假設你是一個小屌絲,那么女神就會對你說:我已經開始化妝了,你到樓下了就給我打電話。

然后你就收拾行頭準備出發,這就是你提交異步任務后還可以做一些自己的事情。

你花了一小時到了女神樓下,打電話給她:女神你好,我到你樓下了。

女神說:你先等著吧,我的妝還沒畫好呢。

于是你開始等待,無盡的等待。這就是不帶超時時間的 future.get() 方法。

如何理解異步編程的Future

也有可能你硬氣一點,對女神說:我最多再等 24 小時哈,超過 24 小時不下樓,我就走了。

這就是帶超時時間的 future.get(timeout,unit) 方法:

如何理解異步編程的Future

結果 24 小時之后,女神還沒下來,你就走了。

當然,還有一種情況就是你到樓下給女神打電話,女神說:哎,今天我男神約我出去看電影,就不和你去吃飯了哈。本來我想提前給你說的,但是我又記不起你電話,只有你打過來我才能告訴你。就這樣,你自己玩去吧。

這就相當于異步任務執行過程中拋出了異常,而你只有在調用了 get 方法(打電話操作)之后才知道原來異常了。

而真正的異步是你不用等我,我好了我就叫你。

就像女神接到男神的電話時說的:我需要一點時間準備一下,你先玩自己的吧,我一會好了給你打電話。

這讓我想起了好萊塢原則:Don't Call Us,We'll Call you!

接下來,讓我們見識一下真正的異步。

什么叫真正的:“你先玩自己的,我一會好了叫你。”

Guava 的 Future

女神說的:“好了叫你”。

就是一種回調機制。說到回調,那么我們就需要在異步任務提交之后,注冊一個回調函數就行。

Google 提供的 Guava 包里面對 JDK 的 Future 進行了擴展:

如何理解異步編程的Future

新增了一個 addListenter 方法,入參是一個 Runnable 的任務類型和一個線程池。

使用方法,先看代碼:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());         ListenableFuture<String> listenableFuture = executor.submit(() -> {             System.out.println(Thread.currentThread().getName()+"-女神:我開始化妝了,好了我叫你。");             TimeUnit.SECONDS.sleep(5);             return "化妝完畢了。";         });          listenableFuture.addListener(() -> {             try {                 System.out.println(Thread.currentThread().getName()+"-future的內容:" + listenableFuture.get());             } catch (Exception e) {                 e.printStackTrace();             }         }, executor);         System.out.println(Thread.currentThread().getName()+"-等女神化妝的時候可以干點自己的事情。");         Thread.currentThread().join();     } }

首先創建線程池的方式變了,需要用 Guava 里面的 MoreExecutors 方法裝飾一下:

ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());

然后用裝飾后的 executor 調用 submit 方法(任意一種),就會返回 ListenableFuture ,拿到這個  ListenableFuture 之后,我們就可以在上面注冊監聽:

如何理解異步編程的Future

所以,上面的程序我們調用的是入參為 callable 類型的接口:

如何理解異步編程的Future

從運行結果可以看出來:獲取運行結果是在另外的線程里面執行的,完全沒有阻塞主線程。

和之前的“假異步”還是有很大區別的。

除了上面的 addListener 方法外,其實我更喜歡用 FutureCallback 的方式。

如何理解異步編程的Future

可以看一下代碼,非常的直觀:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());         ListenableFuture<String> listenableFuture = executor.submit(() -> {             System.out.println(Thread.currentThread().getName()+"-女神:我開始化妝了,好了我叫你。");             TimeUnit.SECONDS.sleep(5);             return "化妝完畢了。";         });         Futures.addCallback(listenableFuture, new FutureCallback<String>() {             @Override             public void onSuccess(@Nullable String result) {                 System.out.println(Thread.currentThread().getName()+"-future的內容:" + result);             }              @Override             public void onFailure(Throwable t) {                 System.out.println(Thread.currentThread().getName()+"-女神放你鴿子了。");                 t.printStackTrace();             }         });         System.out.println(Thread.currentThread().getName()+"-等女神化妝的時候可以干點自己的事情。");         Thread.currentThread().join();     } }

有 onSuccess 方法和 onFailure 方法。

上面的程序輸出結果為:

如何理解異步編程的Future

如果異步任務執行的時候拋出了異常,比如女神被她的男神約走了,異步任務改成這樣:

ListenableFuture<String> listenableFuture = executor.submit(() -> {             System.out.println(Thread.currentThread().getName() + "-女神:我開始化妝了,好了我叫你。");             TimeUnit.SECONDS.sleep(5);             throw new Exception("男神約我看電影,就不和你吃飯了。");         });

最終的運行結果就是這樣:

如何理解異步編程的Future

是的,女神去看電影了。她一定只是不想吃飯而已。

加強版的Future - CompletableFuture

第一小節講的 Future 是 JDK 1.5 時代的產物:

如何理解異步編程的Future

經過了這么多年的發展,Doug Lea 在 JDK 1.8 里面引入了新的 CompletableFuture :

如何理解異步編程的Future

到了 JDK 1.8 時代,這才是真正的異步編程。

CompletableFuture 實現了兩個接口,一個是我們熟悉的 Future ,一個是 CompletionStage。

CompletionStage接口,你看這個接口的名稱中有一個 Stage :

如何理解異步編程的Future

可以把這個接口理解為一個任務的某個階段。所以多個 CompletionStage  鏈接在一起就是一個任務鏈。前一個任務完成后,下一個任務就會自動觸發。

CompletableFuture 里面的方法非常的多。

由于篇幅原因,我就只演示一個方法:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {             System.out.println(Thread.currentThread().getName() + "-女神:我開始化妝了,好了我叫你。");             try {                 TimeUnit.SECONDS.sleep(5);             } catch (InterruptedException e) {                 e.printStackTrace();             }             return "化妝完畢了。";         });          completableFuture.whenComplete((returnStr, exception) -> {             if (exception == null) {                 System.out.println(Thread.currentThread().getName() + returnStr);             } else {                 System.out.println(Thread.currentThread().getName() + "女神放你鴿子了。");                 exception.printStackTrace();             }         });         System.out.println(Thread.currentThread().getName() + "-等女神化妝的時候可以干點自己的事情。");         Thread.currentThread().join();     } }

該方法的執行結果如下:

如何理解異步編程的Future

我們執行的時候并沒有指定用什么線程池,但是從結果可以看到也是異步的執行。

從輸出日志中是可以看出端倪的,ForkJoinPool.commonPool() 是其默認使用的線程池。

如何理解異步編程的Future

當然,我們也可以自己指定。

如何理解異步編程的Future

這個方法在很多開源框架里面使用的還是非常的多的。

接下來主要看看 CompletableFuture 對于異常的處理。我覺得非常的優雅。

不需要 try-catch 代碼塊包裹,也不需要調用 Future.get() 才知道異常了,它提供了一個 handle  方法,可以處理上游異步任務中出現的異常:

public class JDKThreadPoolExecutorTest {      public static void main(String[] args) throws Exception {         CompletableFuture.supplyAsync(() -> {             System.out.println(Thread.currentThread().getName() + "-女神:我開始化妝了,好了我叫你。");             throw new RuntimeException("男神約我看電影了,我們下次再約吧,你是個好人。");         }).handleAsync((result, exception) -> {             if (exception != null) {                 System.out.println(Thread.currentThread().getName() + "-女神放你鴿子了!");                 return exception.getCause();             } else {                 return result;             }         }).thenApplyAsync((returnStr) -> {             System.out.println(Thread.currentThread().getName() + "-" + returnStr);             return returnStr;         });         System.out.println(Thread.currentThread().getName() + "-等女神化妝的時候可以干點自己的事情。");         Thread.currentThread().join();     } }

由于女神在化妝的時候,接到男神的電話約她看電影,就只能放你鴿子了。

所以,上面程序的輸出結果如下:

如何理解異步編程的Future

如果,你順利把女神約出來了,是這樣的:

如何理解異步編程的Future

“如何理解異步編程的Future”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

凤庆县| 格尔木市| 巴楚县| 苍溪县| 墨脱县| 柳林县| 宣城市| 乌审旗| 临漳县| 万载县| 大姚县| 台南县| 金堂县| 阜平县| 区。| 汉寿县| 庆云县| 温宿县| 黄石市| 和田县| 惠州市| 正镶白旗| 刚察县| 齐河县| 内丘县| 涞源县| 绥宁县| 镇远县| 滨海县| 淄博市| 盐城市| 于田县| 乐业县| 仪征市| 白山市| 个旧市| 全南县| 板桥市| 抚远县| 娄烦县| 武宣县|