1. 程式人生 > >多線程並發的使用、學習與測試

多線程並發的使用、學習與測試

分布式系 發現 測試 user 覆蓋 你在 countdown src 調度

  使用

  先說說多線程的使用。多線程本身是與我們開發的項目密不可分的,我們只要提供了接口,那麽他就可能被多個線程同時調用,就會產生並發問題,因此開發者在實際開發中對多線程的理解至關重要。當然,這屬於最基本的認識。

  就這一方面而言,對於開發者及學過並發的學員,平時開發中需要掌握的主要包括:

  1.先說涉及最多的。看到static修飾的變量時,能想到涉及到多線程資源共享。如果是可變的,需要註意變化中的線程安全;如果是不可變的,需要知道如何保證其不可變,而不是簡單的放在那,當前代碼沒有修改他就認為他是不變的。

  2.看到final、單例等定義時,能分析出是否是線程安全的。

  3.在看到集合、Map等結果時候時,知道該選什麽類更適合,以及什麽時候適合使用並發類,什麽時候適合使用普通的類,這裏本質上有對線程封閉的理解。

  4.在遇到SimpleDateFormat等線程不安全的類時,能知道相關的並發風險點

  5.在需要完成一些大任務時,知道如何借助線程池等進行提速,以及借助AQS相關組件進行一些線程的調度

  6.知道常用的線程安全手段,能根據場景去分析是否需要加鎖來保證線程安全。

  說了這麽多,相信很多人已經能感受到並發在日常開發中的存在,也能對並發多一些認識。雖然你可能很難用上並發一些手段,但你需要知道你看到的代碼是否有問題,並發問題更多的存在於一些細節的處理中。這也是很多學員問我為什麽不出一門關於並發實戰性質的課程、而是選擇根據在知識點的講解過程中介紹使用他們的一些場景的願意。同時,由於並發的知識點多而繁雜,單獨一門課程的幾個場景很難覆蓋整個並發知識體系,這樣直接會導致許多人學完了並發卻還是和之前一樣,只是鞏固了一遍知識點,別人問起來心裏依舊很虛。而且,如果為了覆蓋知識點去刻意的在某些場景中使用並發的某些知識點,反而可能會造成很多誤解,效果並不一定理想。

  繼續談多線程的使用。之前介紹的是我們不得不處理的多線程,接下來介紹主動創建的多線程的使用,即我們在代碼裏主動使用多線程去處理一些業務。

  1.提速,充分利用CPU:這個理解起來應該比較容易,同一個任務,多個線程同時處理,讓很多處理並行,達到更快完成的目的。這裏介紹的一個場景,之前項目做大發布,要清洗歷史數據(比如要根據id取出某一行,並計算某個值填進去),由於數據量很大,單線程清洗可能要1個小時左右,為了能盡快完成,不影響發布的時間,因此我們把單線程改為10個線程並行,測試發現10個線程還是有些慢,後來調整為20個線程同時處理,這樣最終在3min左右完成。

  2.降速,協調資源的使用:剛說完提速,這裏又說降速,許多人可能會很奇怪。這裏舉具體例子來說明一下,比如項目裏要發短信,目前有第三方的限制是並發量最多10條,而每天高峰期時段同一時刻需要發送的短信量可能遠高於10條,這時怎麽辦呢?就可以在發短信時引入線程池及多線程池處理,通過semaphore等控制同時發短信的線程不超過10,這樣就起到了降速的作用。

  3.異步處理,解耦。通常我們在做一個業務實現時,都有主流程和次流程之分,比如我們用戶下單時,同時還要完成記錄核心日誌(核心的日誌可能要記錄的數據庫中)以及通知用戶下單完成等非核心業務,這些非核心的業務在處理時,有時可能會很耗時,出任何錯還不能讓主流程失敗,出錯了還需要重試慢慢完成,這種的就可以開啟新的線程去處理,異步解耦,讓主流程快速完成並返回。當然,也不能無限制的開啟新線程,放在線程池裏控制更好。

  許多人不知道如何使用多線程,希望這幾點總結能讓加深你的理解。關於多線程的使用,在什麽場景該使用什麽場景不該使用不是背下來的,而是需要結合實際場景分析,是否需要進行提速、降速、異步處理等,如此才能在工作中較好的使用多線程。

  學習

  之前在面試中並發類問題的準備和學習大致介紹過,這裏就不做過多的講解,做個簡單的總結:

  

技術分享圖片

  還是課程裏這張圖,這張圖不是簡單的覆蓋了並發的知識點,而是希望你能將這幅圖印在腦子裏。當你向別人講述並發時,能先從整體描述並發的幾個大方面,然後能挨個大方面進行細化。這也是你學習並發比較好的方法,腦海中有這個圖,你就可以迅速的把知識點過一遍,哪個知識點模糊了你可以去補充一下就可以了,而不是每次需要學習並發時都是從頭來一遍,而且每次看完都感覺還差點啥。

  這個方法呢,也特別適合在面試中使用。面試官在讓你說說對並發的了解時,你能先整體概括再局部細化,會讓你在這個環節大大加分。不少學員已經從中受益,希望這個也能幫助到你。當然,這種先整體概括再局部細化的方式,也適合大家學習其他方面的知識,會讓你的記憶更深刻,後續鞏固也容易很多。

  測試

  說了並發的使用與學習,之後就是驗證了,也屬於並發的測試。並發的測試通常可以選擇通過接口和方法兩個方向去驗證,可以根據不同需要進行選擇。

  先說根據接口測試。這種的通常要借助工具,Postman、JMeter、Apache Bench(AB)等都是不錯的選擇,其中JMeter、Apache Bench(AB)更適合測試人員來使用,Postman更適合開發人員來使用。

  再說說根據方法測試。方法的測試更適合寫代碼去測試,這裏給出一個模板:

  public class ConcurrencyTest {

  // 請求的總數

  public static int clientTotal = 5000;

  // 同時並發執行的線程數

  public static int threadTotal = 200;

  public static void main(String[] args) throws Exception {

  ExecutorService executorService = Executors.newCachedThreadPool();

  final Semaphoresemaphore = new Semaphore(threadTotal);

  final CountDownLatchcountDownLatch = new CountDownLatch(clientTotal);

  for (int i = 0; i clientTotal ; i++) {

  executorService.execute(() - {

  try {

  semaphore.acquire();

  testMethod();

  semaphore.release();

  } catch (Exception e) {

  log.error(exception, e);

  }

  countDownLatch.countDown();

  });

  }

  countDownLatch.await();

  executorService.shutdown();

  // 所有線程執行完,之後才能執行的部分

  }

  private static void testMethod() {

  // 待驗證的方法

  }

  }

  testMethod這個方法替換為要測試的方法就可以了。如果不需要在所有線程執行完執行某些代碼,可以去掉CountDownLatch的使用;如果不需要控制同一時間同時並行的線程數,可以去掉Semaphore的使用。

  這裏順便補充一個技巧,有時為了讓測試結果更明顯一些,testMethod方法裏可以考慮通過Thread.sleep方法讓測試方法執行的慢一些,讓並發測試的結果更顯著。

  另外希望一定註意的是,在實際項目中,尤其是分布式系統並發場景,核心點要有日誌記錄核心變量的值。但一定不要使用System.out去輸出日誌,而我們通常討論的slf4j、logback等,則是推薦使用的,因為他們會包含我們分析時能用到的:線程名稱、代碼行、及輸出日誌的時間等,System.out不光是沒這些重要信息,而且在輸出時使用了synchronized,這直接導致輸出日誌那裏會出現線程阻塞。更

  實際項目中,尤其是在分布式系統裏,很多問題的分析,我們都要依靠項目運行過程中輸出的日誌。線程名稱、代碼行、輸出日誌的時間、相關的變量值這幾項又是分析過程中最關鍵的,希望大家能引起重視。

  

?

多線程並發的使用、學習與測試