天天看點

并發程式設計的一點思考

并發程式設計可以總結為3個核心問題:

  • 分工:指的是如何高效的拆解任務并配置設定給線程
  • 同步:指的是線程之間如何協作
  • 互斥:則是保證同一時刻隻允許一個線程通路共享資源

JavaSDK并發包很大部分都是按照這三個次元組織的:

進一步的,個人了解,三大核心問題又可以聚焦于2大點:

  • 管程(synchronized、SDK裡的各種Lock)解決同步互斥問題
  • 線程池(CompletableFuture、Fork/Join)實作分工并行

抓住這2點,就可以由點及線,把整個并發程式設計體系串聯起來。個人了解,其實線程池部分嚴格說來應該屬于并行程式設計。

ps.并發 VS 并行 ?

  • 并發,指的是某一時間段内,單核CPU多任務處理,因為線程排程切換使得看起來是同時執行多任務一樣,實際上某一時間隻有一個線程運作。
  • 并行,指的是多CPU情況下,同一時間點多個線程同時運作。

管程

管程是一把解決并發問題的萬能鑰匙。所謂管程,指的是管理共享變量以及對共享變量的操作過程,讓他們支援并發。翻譯為 Java 領域的語言,就是管理類的成員變量和成員方法,讓這個類是線程安全的。

在管程模型裡,共享變量和對共享變量的操作是被封裝起來的,當多個線程同時試圖進入管程内部時,隻允許一個線程進入,其他線程則在入口等待隊列中等待。 ----解決了互斥

條件變量和等待隊列的作用----解決線程同步問題

  • 多個線程同時試圖進入管程内部時,隻允許一個線程進入,其他線程則在入口等待隊列中等待(處于阻塞狀态)。
  • T1線程進入後,檢查條件變量A,如滿足,則執行。如不滿足,T1 需要調用 A.wait()進入條件變量A的等待隊列(釋放了鎖,處于無限等待狀态/有限時等待狀态),進入之後,允許其他線程進入管程。
  • 另外一個線程 T2 執行成功之後,如果條件A對于線程 T1 來說已經滿足了,此時線程 T2 要通知 T1,線程 T2 需要調用 A.notifyAll() 來喚醒 A 等待隊列中的線程,當這些線程得到通知後,會從等待隊列裡面出來,但是出來之後不是馬上執行,而是重新進入到入口等待隊列裡面去競争鎖,線程是否競争到鎖與線程在等待隊列中的先後位置是否相關,又産生了非公平鎖和公平鎖之區分。

java語言内置的管程(synchronized)對 MESA 管程模型進行了精簡。MESA 模型中,條件變量可以有多個,synchronized隻有一個條件變量,隻支援非公平鎖。

正是由于synchronized的局限性,才有了JUC包中的各種lock,JUC包中的AQS就是對MESA管程的實作,然後基于AQS延伸開發出了各種lock和并發工具類,其核心思想的源頭都是管程!

線程池

降低延遲,提高吞吐量

線程池基本原理:美團的這篇文章講的很好---Java線程池實作原理及其在美團業務中的實踐

多線程優化性能的目标說白了就是2點,降低延遲,提高吞吐量,具體做法就是串改并,串行轉換成并行的過程中,一定會涉及到異步化。異步化,是并行方案得以實施的基礎。

  • 簡單的并行任務-----------------------------------------> 線程池 +Future
  • 任務之間有聚合關系(AND 聚合/OR 聚合)--------------------> CompletableFuture (強烈推薦使用!!!)
  • 分治 -------------------------------------------------> Fork/Join (适合處理一些特殊場景,并行計算 海量資料處理)

一些線程池的騷操作:

美團動态線程池

按某個次元順序執行任務的線程池

記憶體安全的阻塞隊列

原生線程池拒絕政策的bug

繼續閱讀