<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 18 場景集合:并發 List、Map的應用場景 ## 引導語 并發 List、Map 使用最多的就是 CopyOnWriteArrayList 和 ConcurrentHashMap,在考慮 API 時,我們也無需遲疑,這兩個并發類在安全和性能方面都很好,我們都可以直接使用。 并發的場景很多,但歸根結底其實就是共享變量被多個線程同時訪問,也就是說 CopyOnWriteArrayList 或 ConcurrentHashMap 會被作為共享變量,本節我們會以流程引擎為案例,現身說法,增加一下大家的工作經驗積累。 流程引擎在實際工作中經常被使用,其主要功能就是對我們需要完成的事情,進行編排和組裝,比如在淘寶下單流程中,我們一共會執行 20 個 Spring Bean,流程引擎就可以幫助我們調起 20 個 Spring Bean,并幫助我們去執行,本文介紹的重點在于如何使用 Map + List 來設計流程引擎的數據結構,以及其中需要注意到的線程安全的問題。 ### 1 嵌套 Map,簡單流程引擎 市面上有很多流程引擎,比如說 Activiti、Flowable、Camunda 等等,功能非常齊全,但我們本小節只實現一種最最簡單的流程引擎,只要能對我們需要完成的事情進行編排,并能依次的調用就行。 #### 1.1 流程引擎設計思路 我們認為每個流程都會做 4 個階段的事情,階段主要是指在整個流程中,大概可以分為幾個大的步驟,每個階段可以等同為大的步驟,分別如下: 1. 參數校驗,主要是對流程的入參數進行校驗; 2. 業務校驗,主要是對當前流程中的業務進行邏輯校驗; 3. 事務中落庫,主要把數據落庫,控制事務; 4. 事務后事件,我們在數據落庫,事務提交之后,可能會做一些其他事情,比如說發消息出來等等。 以上每個大的階段,都會做一些粒度較細的事情,比如說業務校驗,我們可能會對兩個業務對象進行校驗,那么此時業務校驗階段就會做兩件事情,每件具體的事情,我們叫做領域行為,在實際項目中,一個領域行為一般都是一個 Spring Bean。 綜上所述,流程引擎嵌套數據結構就是:流程 -> 階段 -> 領域行為,前者對應后者,都是一對一或者一對多的關系。 我們以在淘寶上買東西時,下單為例,下單指的是我們在淘寶選擇好了商品和優惠券后,點擊購買按鈕時觸發的動作。 為了方便舉例,我們假設在淘寶上買電視和電影票,在后端,會分別對應著兩個下單流程,我們畫圖示意一下: ![](https://img.kancloud.cn/77/22/7722f5bdc0476561018b6d816cef873e_1272x927.jpg) 上圖中,左右兩個黑色長方形大框代表著兩個流程,流程下面有多個階段,階段用藍色表示,每個階段下面有多個領域行為,用紅色表示。 可以看到兩個流程中,都包含有四個階段,階段都是相同的,但每個階段中的領域行為,有的相同,有的卻是特有的。 三個概念,每個概念層層嵌套,整體組裝起來,用來表示一個流程,那么這個數據結構,我們應該如何表示呢? 使用 Map + List 即可! #### 1.2 數據結構的定義 流程的數據結構定義分成兩步: 1. 定義出階段、領域行為基礎概念; 2. 把階段、領域行為、流程概念組合起來,定義出流程的數據結構。 首先給階段定義一個枚舉,如下 StageEnum 代表流程中的階段或步驟: ``` public enum StageEnum { PARAM_VALID("PARAM_VALID", "參數校驗"), BUSINESS_VALID("BUSINESS_VALID", "業務校驗"), IN_TRANSACTION("IN_TRANSACTION", "事務中落庫"), AFTER_TRANSACTION("AFTER_TRANSACTION", "事務后事件"), ; private String code; private String desc; StageEnum(String code, String desc) { this.code = code; this.desc = desc; } } ``` 領域行為我們無需定義,目前通用的技術框架都是 Spring Boot,領域行為都是 Spring Bean,為了簡單起見,我們給領域行為定義了一個接口,每個領域行為都要實現這個接口,方便我們編排,接口如下: ``` /** * 領域行為 * author wenhe * date 2019/8/11 */ public interface DomainAbilityBean { /** * 領域行為的方法入口 */ FlowContent invoke(FlowContent content); } ``` 接著我們使用 Map + List 來定義流程,定義如下: ``` /** * 第一個 key 是流程的名字 * 第二個 map 的 key 是階段,為 StageEnum 枚舉,值為多個領域行為的集合 */ Map<String,Map<StageEnum,List<DomainAbilityBean>>> flowMap ``` 至此,我們定義出了,簡單流程引擎的數據結構,流程引擎看著很復雜,利用 Map + List 的組合,就巧妙的定義好了。 ### 2 容器初始化時,本地緩存使用 我們定義好 Map 后,我們就需要去使用他,我們使用的大體步驟為: 1. 項目啟動時,把所有的流程信息初始化到 flowMap(剛剛定義的流程的數據結構叫做 flowMap) 中去,可能是從數據庫中加載,也可能是從 xml 文件中加載; 2. 查找流程時,直接從 flowMap 中獲取即可。 #### 2.1 初始化 以上兩步最為關鍵的點就是 flowMap 必須是可以隨時訪問到的,所有我們會把 flowMap 作為共享變量使用,也就是會被 static final 關鍵字所修飾,我們首先來 mock 一下把所有信息初始化到 flowMap 中去的代碼,如下: ``` @Component public class FlowCenter { /** * flowMap 是共享變量,方便訪問,并且是 ConcurrentHashMap */ public static final Map<String, Map<StageEnum, List<DomainAbilityBean>>> flowMap = Maps.newConcurrentMap(); /** * PostConstruct 注解的意思就是 * 在容器啟動成功之后,執行 init 方法,初始化 flowMap */ @PostConstruct public void init() { // 初始化 flowMap,可能是從數據庫,或者 xml 文件中加載 map } } ``` 以上代碼,關鍵地方在于三點: 1. flowMap 被 static final 修飾,是個共享變量,方便訪問; 2. flowMap 是 ConcurrentHashMap,所以我們所有的操作都無需加鎖,比如我們在 init 方法中,對 flowMap 進行初始化,就無需加鎖,因為 ConcurrentHashMap 本身已經保證了線程安全; 3. 這里我們初始化的時機是在容器啟動的時候,在實際的工作中,我們經常在容器啟動的時候,把不會經常發生變動的數據,放到類似 List、Map 這樣的共享變量中,這樣當我們頻繁要使用的時候,直接從內存中讀取即可。 #### 2.2 使用 那我們實際使用的時候,只需要告訴 flowMap 當前是那個流程的那個階段,就可以返回該流程該階段下面的所有領域行為了,我們寫了一個流程引擎使用的工具類入口,如下: ``` // 流程引擎對外的 API public class FlowStart { /** * 流程引擎開始 * * @param flowName 流程的名字 */ public void start(String flowName, FlowContent content) { invokeParamValid(flowName, content); invokeBusinessValid(flowName, content); invokeInTramsactionValid(flowName, content); invokeAfterTramsactionValid(flowName, content); } // 執行參數校驗 private void invokeParamValid(String flowName, FlowContent content) { stageInvoke(flowName, StageEnum.PARAM_VALID, content); } // 執行業務校驗 private void invokeBusinessValid(String flowName, FlowContent content) { stageInvoke(flowName, StageEnum.BUSINESS_VALID, content); } // 執行事務中 private void invokeInTramsactionValid(String flowName, FlowContent content) { stageInvoke(flowName, StageEnum.IN_TRANSACTION, content); } // 執行事務后 private void invokeAfterTramsactionValid(String flowName, FlowContent content) { stageInvoke(flowName, StageEnum.AFTER_TRANSACTION, content); } // 批量執行 Spring Bean private void stageInvoke(String flowName, StageEnum stage, FlowContent content) { List<DomainAbilityBean> domainAbilitys = FlowCenter.flowMap.getOrDefault(flowName, Maps.newHashMap()).get(stage); if (CollectionUtils.isEmpty(domainAbilitys)) { throw new RuntimeException("找不到該流程對應的領域行為" + flowName); } for (DomainAbilityBean domainAbility : domainAbilitys) { domainAbility.invoke(content); } } } ``` 從代碼中可以看到,我們在流程引擎的入口,只要根據參數校驗、業務校驗、事務中、事務后四個階段,從 flowMap 中得到領域行為的集合,然后對領域行為進行順序執行即可。 我們在使用時,直接使用上述類的 start 方法即可。 當然以上演示的流程引擎只是一個大的框架,還有很多地方需要改進的地方,比如如何找到 flowName,如何初始化 flowMap,但這些都不是本節重點,本節主要想通過流程引擎案例來說明幾點: 1. 把 List 和 Map 作為共享變量非常常見,就像咱們這種項目啟動時,從數據庫中把數據撈出來,然后封裝成 List 或 Map 的結構,這樣做的優點就是節約資源,不用每次用的時候都去查數據庫,直接從內存中獲取即可; 2. 并發場景下,我們可以放心的使用 CopyOnWriteArrayList 和 ConcurrentHashMap 兩個并發類,首先用 static final 對兩者進行修飾,使其成為共享變量,接著在寫入或者查詢的時候,無需加鎖,兩個 API 內部已經實現了加鎖的功能了; 3. 有一點需要澄清一下,就是 CopyOnWriteArrayList 和 ConcurrentHashMap 只能作為單機的共享變量,如果是分布式系統,多臺機器的情況下,這樣做不行了,需要使用分布式緩存了。 #### 3 總結 本節內容,以流程引擎為例,說明了如何使用 Map + List 的嵌套結構設計流程引擎,以及在并發情況下,如何安全的使用 List 和 Map。 本案列是高并發項目的真實案例,感興趣的同學可以在此流程引擎框架基礎上進行細節補充,實現可運行的流程引擎。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看