成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

多線程不得不聊的Future類

開(kāi)發(fā) 前端
在高性能編程中,并發(fā)編程已經(jīng)成為了極為重要的一部分。在單核CPU性能已經(jīng)趨于極限時(shí),我們只能通過(guò)多核來(lái)進(jìn)一步提升系統(tǒng)的性能,因此就催生了并發(fā)編程。

 

[[392402]]

本文轉(zhuǎn)載自微信公眾號(hào)「三太子敖丙」,作者三太子敖丙 。轉(zhuǎn)載本文請(qǐng)聯(lián)系三太子敖丙公眾號(hào)。

在高性能編程中,并發(fā)編程已經(jīng)成為了極為重要的一部分。在單核CPU性能已經(jīng)趨于極限時(shí),我們只能通過(guò)多核來(lái)進(jìn)一步提升系統(tǒng)的性能,因此就催生了并發(fā)編程。

由于并發(fā)編程比串行編程更困難,也更容易出錯(cuò),因此,我們就更需要借鑒一些前人優(yōu)秀的,成熟的設(shè)計(jì)模式,使得我們的設(shè)計(jì)更加健壯,更加完美。

而Future模式,正是其中使用最為廣泛,也是極為重要的一種設(shè)計(jì)模式。今天就跟阿丙了解一手Future模式!

生活中的Future模式

為了更快的了解Future模式,我們先來(lái)看一個(gè)生活中的例子。

場(chǎng)景1:

午飯時(shí)間到了,同學(xué)們要去吃飯了,小王下樓,走了20分鐘,來(lái)到了肯德基,點(diǎn)餐,排隊(duì),吃飯一共花了20分鐘,又花了20分鐘走回公司繼續(xù)工作,合計(jì)1小時(shí)。

場(chǎng)景2

午飯時(shí)間到了,同學(xué)們要去吃飯了,小王點(diǎn)了個(gè)肯德基外賣,很快,它就拿到了一個(gè)訂單(雖然訂單不能當(dāng)飯吃,但是有了訂單,還怕吃不上飯嘛)。接著小王可以繼續(xù)干活,30分鐘后,外賣到了,接著小王花了10分鐘吃飯,接著又可以繼續(xù)工作了,成功的卷到了隔壁的小汪。

很明顯,在這2個(gè)場(chǎng)景中,小王的工作時(shí)間更加緊湊,特別是那些排隊(duì)的時(shí)間都可以讓外賣員去干,因此可以更加專注于自己的本職工作。聰明的你應(yīng)該也已經(jīng)體會(huì)到了,場(chǎng)景1就是典型的函數(shù)同步調(diào)用,而場(chǎng)景2是典型的異步調(diào)用。

而場(chǎng)景2的異步調(diào)用,還有一個(gè)特點(diǎn),就是它擁有一個(gè)返回值,這個(gè)返回值就是我們的訂單。這個(gè)訂單很重要,憑借著這個(gè)訂單,我們才能夠取得當(dāng)前這個(gè)調(diào)用所對(duì)應(yīng)的結(jié)果。

這里的訂單就如同F(xiàn)uture模式中的Future,這是一個(gè)合約,一份承諾。雖然訂單不能吃,但是手握訂單,不怕沒(méi)吃的,雖然Future不是我們想要的結(jié)果,但是拿著Future就能在將來(lái)得到我們想要的結(jié)果。

因此,F(xiàn)uture模式很好的解決了那些需要返回值的異步調(diào)用。

Future模式中的主要角色

一個(gè)典型的Future模式由以下幾個(gè)部分組成:

  • Main:系統(tǒng)啟動(dòng),調(diào)用Client發(fā)出請(qǐng)求
  • Client:返回Data對(duì)象,立即返回FutureData,并開(kāi)啟ClientThread線程裝配RealData
  • Data:返回?cái)?shù)據(jù)的接口
  • FutureData:Future數(shù)據(jù),構(gòu)造很快,但是是一個(gè)虛擬的數(shù)據(jù),需要裝配RealData,好比一個(gè)訂單
  • RealData:真實(shí)數(shù)據(jù),其構(gòu)造是比較慢的,好比上面例子中的肯德基午餐。

它們之間的相互關(guān)系如下圖:

其中,值得注意是Data,RealData和FutureData。這是一組典型的代理模式,Data接口表示對(duì)外數(shù)據(jù),RealData表示真實(shí)的數(shù)據(jù),就好比午餐,獲得它的成本比較高,需要很多時(shí)間;相對(duì)的FutureData作為RealData的代理,類似于一個(gè)訂單/契約,通過(guò)FutureData,可以在將來(lái)獲得RealData。

因此,F(xiàn)uture模式本質(zhì)上是代理模式的一種實(shí)際應(yīng)用。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Future模式

根據(jù)上面的設(shè)計(jì),讓我們來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的代理模式吧!

首先是Data接口,代表數(shù)據(jù):

  1. public interface Data { 
  2.     public String getResult (); 

接著是FutureData,也是整個(gè)Future模式的核心:

  1. public class FutureData implements Data { 
  2.     // 內(nèi)部需要維護(hù)RealData 
  3.     protected RealData realdata = null;           
  4.     protected boolean isReady = false
  5.     public synchronized void setRealData(RealData realdata) { 
  6.         if (isReady) {  
  7.             return
  8.         } 
  9.         this.realdata = realdata; 
  10.         isReady = true
  11.         //RealData已經(jīng)被注入,通知getResult() 
  12.         notifyAll();                                
  13.     } 
  14.     //會(huì)等待RealData構(gòu)造完成 
  15.     public synchronized String getResult() {          
  16.         while (!isReady) { 
  17.             try { 
  18.                 //一直等待,直到RealData被注入 
  19.                 wait();                            
  20.             } catch (InterruptedException e) { 
  21.             } 
  22.         } 
  23.         //真正需要的數(shù)據(jù)從RealData獲取 
  24.         return realdata.result;                       
  25.     } 

下面是RealData:

  1. public class RealData implements Data { 
  2.     protected final String result; 
  3.     public RealData(String para) { 
  4.         StringBuffer sb=new StringBuffer(); 
  5.         //假設(shè)這里很慢很慢,構(gòu)造RealData不是一個(gè)容易的事 
  6.         result =sb.toString(); 
  7.     } 
  8.     public String getResult() { 
  9.         return result; 
  10.     } 

然后從Client得到Data:

  1. public class Client { 
  2.     //這是一個(gè)異步方法,返回的Data接口是一個(gè)Future 
  3.     public Data request(final String queryStr) { 
  4.         final FutureData future = new FutureData(); 
  5.         new Thread() {                                       
  6.             public void run() {                      
  7.                 // RealData的構(gòu)建很慢,所以在單獨(dú)的線程中進(jìn)行 
  8.                 RealData realdata = new RealData(queryStr); 
  9.                 //setRealData()的時(shí)候會(huì)notify()等待在這個(gè)future上的對(duì)象 
  10.                 future.setRealData(realdata); 
  11.             }                                                
  12.         }.start(); 
  13.         // FutureData會(huì)被立即返回,不會(huì)等待RealData被構(gòu)造完 
  14.         return future;                           
  15.     } 

最后一個(gè)Main函數(shù),把所有一切都串起來(lái):

  1. public static void main(String[] args) { 
  2.     Client client = new Client(); 
  3.     //這里會(huì)立即返回,因?yàn)榈玫降氖荈utureData而不是RealData 
  4.     Data data = client.request("name"); 
  5.     System.out.println("請(qǐng)求完畢"); 
  6.     try { 
  7.         //這里可以用一個(gè)sleep代替了對(duì)其他業(yè)務(wù)邏輯的處理 
  8.         //在處理這些業(yè)務(wù)邏輯的過(guò)程中,RealData被創(chuàng)建,從而充分利用了等待時(shí)間 
  9.         Thread.sleep(2000); 
  10.     } catch (InterruptedException e) { 
  11.     } 
  12.     //使用真實(shí)的數(shù)據(jù),如果到這里數(shù)據(jù)還沒(méi)有準(zhǔn)備好,getResult()會(huì)等待數(shù)據(jù)準(zhǔn)備完,再返回 
  13.     System.out.println("數(shù)據(jù) = " + data.getResult()); 

這是一個(gè)最簡(jiǎn)單的Future模式的實(shí)現(xiàn),雖然簡(jiǎn)單,但是已經(jīng)包含了Future模式中最精髓的部分。對(duì)大家理解JDK內(nèi)部的Future對(duì)象,有著非常重要的作用。

Java中的Future模式

Future模式是如此常用,在JDK內(nèi)部已經(jīng)有了比較全面的實(shí)現(xiàn)和支持。下面,讓我們一起看看JDK內(nèi)部的Future實(shí)現(xiàn):

首先,JDK內(nèi)部有一個(gè)Future接口,這就是類似前面提到的訂單,當(dāng)然了,作為一個(gè)完整的商業(yè)化產(chǎn)品,這里的Future的功能更加豐富了,除了get()方法來(lái)獲得真實(shí)數(shù)據(jù)以外,還提供一組輔助方法,比如:

  • cancel():如果等太久,你可以直接取消這個(gè)任務(wù)
  • isCancelled():任務(wù)是不是已經(jīng)取消了
  • isDone():任務(wù)是不是已經(jīng)完成了
  • get():有2個(gè)get()方法,不帶參數(shù)的表示無(wú)窮等待,或者你可以只等待給定時(shí)間

下面代碼演示了這個(gè)Future的使用方法:

  1. //異步操作 可以用一個(gè)線程池 
  2.   ExecutorService executor = Executors.newFixedThreadPool(1); 
  3.   //執(zhí)行FutureTask,相當(dāng)于上例中的 client.request("name") 發(fā)送請(qǐng)求 
  4.   //在這里開(kāi)啟線程進(jìn)行RealData的call()執(zhí)行 
  5.   Future<String> future = executor.submit(new RealData("name")); 
  6.   System.out.println("請(qǐng)求完畢,數(shù)據(jù)準(zhǔn)備中"); 
  7.   try { 
  8.       //這里依然可以做額外的數(shù)據(jù)操作,這里使用sleep代替其他業(yè)務(wù)邏輯的處理 
  9.       Thread.sleep(2000); 
  10.   } catch (InterruptedException e) { 
  11.   } 
  12.   //如果此時(shí)call()方法沒(méi)有執(zhí)行完成,則依然會(huì)等待 
  13.   System.out.println("數(shù)據(jù) = " + future.get()); 

整個(gè)使用過(guò)程非常簡(jiǎn)單,下面我們來(lái)分析一下executor.submit()里面究竟發(fā)生了什么:

  1. public <T> Future<T> submit(Callable<T> task) { 
  2.     if (task == null) throw new NullPointerException(); 
  3.     // 根據(jù)Callable對(duì)象,創(chuàng)建一個(gè)RunnableFuture,這里其實(shí)就是FutureTask 
  4.     RunnableFuture<T> ftask = newTaskFor(task); 
  5.     //將ftask推送到線程池 
  6.     //在新線程中執(zhí)行的,就是run()方法,在下面的代碼中有給出 
  7.     execute(ftask); 
  8.     //返回這個(gè)Future,將來(lái)通過(guò)這個(gè)Future就可以得到執(zhí)行的結(jié)果 
  9.     return ftask; 
  10. protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { 
  11.     return new FutureTask<T>(callable); 

最關(guān)鍵的部分在下面,F(xiàn)utureTask作為一個(gè)線程單獨(dú)執(zhí)行時(shí),會(huì)將結(jié)果保存到outcome中,并設(shè)置任務(wù)的狀態(tài),下面是FutureTask的run()方法:

從FutureTask中獲得結(jié)果的實(shí)現(xiàn)如下:

  1. public V get() throws InterruptedException, ExecutionException { 
  2.        int s = state; 
  3.        //如果沒(méi)有完成,就等待,回到用park()方法阻塞線程 
  4.        //同時(shí),所有等待線程會(huì)在FutureTask的waiters字段中排隊(duì)等待 
  5.        if (s <= COMPLETING) 
  6.            s = awaitDone(false, 0L); 
  7.        return report(s); 
  8.    } 
  9.    private V report(int s) throws ExecutionException { 
  10.        //outcome里保存的就是最終的計(jì)算結(jié)果 
  11.        Object x = outcome; 
  12.        if (s == NORMAL) 
  13.            //正常完成,就返回outcome 
  14.            return (V)x; 
  15.        //如果沒(méi)有正常完成, 比如被用戶取消了,或者有異常了,就拋出異常 
  16.        if (s >= CANCELLED) 
  17.            throw new CancellationException(); 
  18.        throw new ExecutionException((Throwable)x); 
  19.    } 

Future模式的高階版本—— CompletableFuture

Future模式雖然好用,但也有一個(gè)問(wèn)題,那就是將任務(wù)提交給線程后,調(diào)用線程并不知道這個(gè)任務(wù)什么時(shí)候執(zhí)行完,如果執(zhí)行調(diào)用get()方法或者isDone()方法判斷,可能會(huì)進(jìn)行不必要的等待,那么系統(tǒng)的吞吐量很難提高。

為了解決這個(gè)問(wèn)題,JDK對(duì)Future模式又進(jìn)行了加強(qiáng),創(chuàng)建了一個(gè)CompletableFuture,它可以理解為Future模式的升級(jí)版本,它最大的作用是提供了一個(gè)回調(diào)機(jī)制,可以在任務(wù)完成后,自動(dòng)回調(diào)一些后續(xù)的處理,這樣,整個(gè)程序可以把“結(jié)果等待”完全給移除了。

下面來(lái)看一個(gè)簡(jiǎn)單的例子:

在這個(gè)例子中,首先以getPrice()為基礎(chǔ)創(chuàng)建一個(gè)異步調(diào)用,接著,使用thenAccept()方法,設(shè)置了一個(gè)后續(xù)的操作,也就是當(dāng)getPrice()執(zhí)行完成后的后續(xù)處理。

不難看到,CompletableFuture比一般的Future更具有實(shí)用性,因?yàn)樗梢栽贔uture執(zhí)行成功后,自動(dòng)回調(diào)進(jìn)行下一步的操作,因此整個(gè)程序不會(huì)有任何阻塞的地方(也就是說(shuō)你不用去到處等待Future的執(zhí)行,而是讓Future執(zhí)行成功后,自動(dòng)來(lái)告訴你)。

以上面的代碼為例,CompletableFuture之所有會(huì)有那么神奇的功能,完全得益于AsyncSupply類(由上述代碼中的supplyAsync()方法創(chuàng)建)。

AsyncSupply在執(zhí)行時(shí),如下所示:

  1. public void run() { 
  2.             CompletableFuture<T> d; Supplier<T> f; 
  3.             if ((d = dep) != null && (f = fn) != null) { 
  4.                 dep = null; fn = null
  5.                 if (d.result == null) { 
  6.                     try { 
  7.                         //這里就是你要執(zhí)行的異步方法 
  8.                         //結(jié)果會(huì)被保存下來(lái),放到d.result字段中 
  9.                         d.completeValue(f.get()); 
  10.                     } catch (Throwable ex) { 
  11.                         d.completeThrowable(ex); 
  12.                     } 
  13.                 } 
  14.                 //執(zhí)行成功了,進(jìn)行后續(xù)處理,在這個(gè)后續(xù)處理中,就會(huì)調(diào)用thenAccept()中的消費(fèi)者 
  15.                 //這里就相當(dāng)于Future完成后的通知 
  16.                 d.postComplete(); 
  17.             } 
  18.         } 

繼續(xù)看d.postComplete(),這里會(huì)調(diào)用后續(xù)一系列操作

  1. final void postComplete() { 
  2.              //省略部分代碼,重點(diǎn)在tryFire()里 
  3.              //在tryFire()里,真正觸發(fā)了后續(xù)的調(diào)用,也就是thenAccept()中的部分 
  4.              f = (d = h.tryFire(NESTED)) == null ? this : d; 
  5.          } 
  6.      } 
  7.  } 

絮叨

今天,我們主要介紹Future模式,我們從一個(gè)最簡(jiǎn)單的Future模式開(kāi)始,逐步深入,先后介紹了JDK內(nèi)部的Future模式實(shí)現(xiàn),以及對(duì)Future模式的進(jìn)化版本CompletableFuture做了簡(jiǎn)單的介紹。對(duì)

于多線程開(kāi)發(fā)而言,F(xiàn)uture模式的應(yīng)用極其廣泛,可以說(shuō)這個(gè)模式已經(jīng)成為了異步開(kāi)發(fā)的基礎(chǔ)設(shè)施。

 

責(zé)任編輯:武曉燕 來(lái)源: 三太子敖丙
相關(guān)推薦

2010-11-02 14:51:11

職場(chǎng)

2022-07-29 11:19:38

日志框架實(shí)踐

2020-06-15 08:19:00

ZooKeeperEureka

2011-03-31 10:46:54

LinuxCLI軟件

2019-11-14 15:38:46

AndroidRelease項(xiàng)目

2020-07-09 12:50:29

JVM內(nèi)存管理Java

2019-12-24 14:04:59

PythonExcel數(shù)據(jù)處理

2019-10-18 17:55:03

安全運(yùn)營(yíng)

2010-05-10 13:01:03

OracleDBA面試

2010-05-21 09:40:57

MySQL出錯(cuò)代碼列表

2010-05-25 09:58:43

MySQL數(shù)據(jù)庫(kù)

2010-05-26 15:58:52

MySQL遠(yuǎn)程連接

2018-05-23 10:04:24

MySQL查詢優(yōu)化

2017-08-16 18:03:12

Docker安全工具容器

2014-06-16 10:03:54

分組交換

2009-11-03 17:43:19

Oracle用戶授權(quán)

2022-01-27 07:39:57

進(jìn)程操作系統(tǒng)Linux

2010-04-21 17:19:29

Oracle創(chuàng)建

2019-12-10 15:30:27

SaaSIaaS云計(jì)算

2020-06-04 13:52:00

CRM選型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品日韩在线观看一区二区 | 久久久久黄色 | 欧美黑人一区二区三区 | 亚洲精品黄 | www.亚洲.com | www.精品国产 | 国产午夜三级一区二区三 | 精品一区二区三区四区五区 | 中文字幕在线一区 | 国产精品3区 | 欧美大片一区二区 | av中文字幕在线观看 | 欧美日韩国产精品激情在线播放 | 亚洲精品日韩精品 | 男人天堂手机在线视频 | 日韩在线一区二区三区 | 亚洲免费在线 | 午夜精品久久久久久久久久久久久 | 天天影视网天天综合色在线播放 | 日韩av最新网址 | 鲁大师一区影视 | 国产精品久久久久久婷婷天堂 | 99久久影院| 国外成人在线视频网站 | 精品欧美一区二区在线观看视频 | 久久伊人精品 | 九一视频在线观看 | 久久久www | 在线看亚洲 | 日韩国产中文字幕 | 欧美国产亚洲一区二区 | www.日韩 | 亚洲免费观看视频 | 午夜精品福利视频 | 中文字幕日本一区二区 | 欧美日本在线观看 | 91精品国产91久久久久久三级 | 久久国产激情视频 | 国产精品乱码一区二区三区 | 精品成人av| 欧美激情精品久久久久久变态 |