<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      項目中一次單例模式的優化

      封裝了一個 ConsumerClient, 項目中有多個任務需求需要使用到 Kafaka, 為了保證項目中只有一個 Kafka 連接實例, 提供一個全局訪問點, 所以我使用單例模式來創建.

      一、線程不安全的懶漢單例

      直接使用懶漢單例, 這樣如果系統中沒有連接 Kafka 需求時就不需要 創建連接了.最簡單直接的方法.

      假設此時有 A、B 兩個線程需要調用 getInstance(), 并且 A 線程調用執行到代碼 1 處的同時 B 線程執行到 代碼 2 處,
      線程 A 看見 consumerClient 沒有被創建, 而線程 B 又正在創建實例, 由此引發線程被創建兩次, 這是一個不安全的線程;

      public class ProducerClient {
          private static ProducerClient producerClient;
          public static ConsumerClient getInstance() {
              if (consumerClient == null) {                // 1: A 線程 執行
                  consumerClient = new ConsumerClient();   // 2: B 線程 執行
              }
              return consumerClient;
          }
      }
      

      二、線程安全的單例

      所以接下來最簡單的方法就是對 getInstance() 做同步處理來實現線程安全, 這樣每次只有一個線程能夠進入 getInstance(), 其他線程就需要等待了;

      但是如果getInstance()被多個線程頻繁調用, synchronized 也會隨之帶來性能開銷 (其實這里已經足夠滿足當前項目中使用了, 另外JDK從1.6開始已經做了很大優化);

      這里帶來的問題時盡管 ProducerClient 實例已將被創建, 但是后面線程每次調用 getInstance() 都需要獲取鎖.

      即如果 B 線程在 getInstance() 中, 則 A 需要在方法外等待;

      public class ProducerClient {
          private static ProducerClient producerClient;
          
          public synchronized static  ConsumerClient getInstance() {  // 加鎖處理
              if (consumerClient == null) {
                  consumerClient = new ConsumerClient();
              }
              return consumerClient;
          }
      }
      

      參考

      三、使用雙重校驗鎖優化

      不過我在閱讀 <<Java并發編程的藝術>>第3章 Java內存模型 中提到早期人們為了應對 synchronized 帶來的性能瓶頸問題, 降低同步開銷,
      提出了一個"聰明"的技巧: 雙重檢查校驗鎖(Double-Checked Locking).

      雙重檢查, 即兩次檢查實例是否創建.

      • 加鎖處理保證了只有一個線程能創建對象;
      • 第一次檢查的作用是為了實例如果被創建, 執行 getInstance()就不需要獲取鎖了, 直接返回實例對象.
      public class ProducerClient {
          private static ProducerClient producerClient;
          
          public static ConsumerClient getInstance() {
              if (consumerClient == null) {                        // 1: 第一次檢查對象是否創建
                  synchronized(ProducerClient.class) {             // 2: 沒有創建, 加鎖處理
                      if (consumerClient == null) {                // 3: 再一次檢查對象是否創建
                          consumerClient = new ConsumerClient();   // 4: 創建對象
                      }
                  }
              }
              return consumerClient;
          }
      }
      

      這里看著很完美啊, 我也沒有看出有什么問題, 繼續看書;
      重排序, 是重排序問題, 代碼 4 可以被一些 JIT 編譯器重排序

      如下, 2 和 3 之間沒有數據依賴, 滿足 as-if-serial 語義, 符合happens-before 規則, 可以被重排序.

      單程序倒是沒有問題, 這里重排序后就導致問題:

      對象實例還沒有完成初始化就返回, 導致別的線程獲取到一個還沒有完成初始化的對象.

      解決方案有兩個:

      1. 不允許 2 3 重排序;
      2. 允許2、3 重排, 當是不允許別的線程 "看到" 這個重排序.
      // 拆解   consumerClient = new ConsumerClient();   // 4: 創建對象
      
      memory = allocate(); // 1:分配對象的內存空間
      ctorInstance(memory); // 2:初始化對象
      instance = memory; // 3:設置instance指向剛分配的內存地址
      
      // 重排序
      
      memory = allocate();     // 1:分配對象的內存空間
      instance = memory;       // 3:設置instance指向剛分配的內存地址
      
      ctorInstance(memory);    // 2:初始化對象
      

      參考

      四、雙重校驗鎖的優化

      結局方案有兩個, 一是基于 volatile , 二是基于類初始化的解決方案.

      基于 volatile 關鍵字

      (JDK 5 之后的JSR-133 內存模型規范, 這個規范增強了 volatile 的語義)
      申明之后, 在多線程環境中此重排序會被禁止;

      
      public class ProducerClient {
          private volatile static ProducerClient producerClient;  // volatile 關鍵字
          
          public static ConsumerClient getInstance() {
              if (consumerClient == null) {
                  synchronized(ProducerClient.class) {             // 2: 沒有創建, 加鎖處理
                      if (consumerClient == null) {                // 3: 再一次檢查對象是否創建
                          consumerClient = new ConsumerClient();   // 4: 創建對象
                      }
                  }
              }
              return consumerClient;
          }
      }
      

      基于類初始化的方案 (InstanceHolder單例模式 )

      JVM在類的初始化階段(即在Class被加載后,且被線程使用之前),會執行類的初始化。在
      執行類的初始化期間,JVM會去獲取一個鎖。這個鎖可以同步多個線程對同一個類的初始化。

      基于這個特性,可以實現另一種線程安全的延遲初始化方案(這個方案被稱之為
      Initialization On Demand Holder idiom)。

      這個方案解釋起來比較復雜, 感興趣可以去看看書中的分析, 這里不在贅述.

      public class ProducerClient {
          private static class InstanceHolder{
              public static ProducerClient client = new ProducerClient();
          }
          
          public static ConsumerClient getInstance() {
              return InstanceHolder.client; // 這里將導致 InstanceHolder 類被初始化
          }
      }
      

      如何選擇這兩個方案

      字段延遲初始化降低了初始化類或創建實例的開銷,但增加了訪問被延遲初始化的字段
      的開銷。在大多數時候,正常的初始化要優于延遲初始化。

      • 需要對 實例字段 使用線程安全的延遲初始化,基于volatile的方案;
      • 需要對 靜態字段 使用線程安全的延遲初始化,基于類初始化的方案.

      我最終選用使用 volatile 優化, 而不選類初始化的方案, 原因是因為我在new ProducerClient() 中會去連接 Kafka, 避免造成每次啟動項目都去連接, 有時候不需要連接 Kafka.

      posted @ 2021-04-11 11:10  小鳴Cycling  閱讀(125)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品麻豆成人av网| 国产精品亚洲综合色区丝瓜| 亚洲欧美日韩精品色xxx| 国产一区二区三区精品片| 秦皇岛市| 国产亚洲精品成人aa片新蒲金| 日本va欧美va精品发布| 熟妇无码熟妇毛片| 少妇被粗大的猛进69视频| 亚洲日本乱码熟妇色精品| 少妇高潮喷水正在播放| 国产高清自产拍av在线| 1精品啪国产在线观看免费牛牛| 日韩国产亚洲一区二区三区| 欧美一区二区三区性视频| 欧美性猛交xxxx乱大交丰满| 国产精品国产三级国av| 梁河县| 亚洲一区久久蜜臀av| 日本深夜福利在线观看| 无码人妻丝袜在线视频| 天堂资源国产老熟女在线| 特黄少妇60分钟在线观看播放| 在线精品另类自拍视频| 美女裸体黄网站18禁止免费下载| 国产不卡一区二区精品| 久久精品熟女亚洲av麻| 97人妻免费碰视频碰免| 久久亚洲国产五月综合网| 日产国产精品亚洲系列| 日韩熟女乱综合一区二区| 中国熟女仑乱hd| 玩弄漂亮少妇高潮白浆| 亚洲欧美色综合影院| 4hu四虎永久在线观看| 国产精品免费AⅤ片在线观看 | 亚洲成人四虎在线播放| 国产欧美在线一区二区三| 亚洲精品成人片在线观看精品字幕| 国产亚洲精品久久久久久大师| 亚洲欧美在线一区中文字幕|