Java並發編程學習路線


一年前由於工作需要從微軟技術棧入坑Java,並陸陸續續做了一個Java后台項目,目前在搞Scala+Java混合的后台開發,一直覺得並發編程是所有后台工程師的基本功,所以也學習了小一年Java的並發工具,對整體的並發理解乃至分布式都有一定的提高,所以想和大家分享一下。

我的學習路線

首先說說學習路線,我一開始是直接上手JCIP(Java Concurrency in Practice),發現不是很好懂,把握不了那本書的主線,所以思索着從國內的作者開始先,所以便讀了下方騰飛的《Java並發編程的藝術》的,雖然豆瓣上的評價一般,但是對於構建Java並發的整體映像還是有所提高的,至少我知道了有哪些東西要深入學習。接着我想加強下並發的理論,繼續讀了The Art of Multiprocessor Programming,這本書比較艱澀,不是很好懂,但是過一遍還是好處多多,建議初學者了解下概念的過過,后期可以再來翻看。有了以上兩步的支持,接下來就又開始啃JCIP了,發現比以前有了不同的感覺,我能比較輕松的跟上書的脈絡,知道書的整體框架,讀起來不那么費勁了,這本書號稱Java並發編程的聖經,確實可以看出作者有很豐富的並發實踐經驗。再后來我過了一遍Oracle官網上的Java Tutorial關於並發的那一章,發現講的也不錯,對於了解基礎庫有哪些組件幫助挺大。

到了這一步,接下來怎么繼續提高呢?我發現了一本很有趣的書,《七周七並發模型》,之前的視野一直是在Java並發編程的工具包中深入了解,感覺,應該跳出來,從模型的角度看看各個語言的並發實現的原理,我目前正處於這一步,發現很有意思,第一章講Java的線程和鎖這個模型就感覺很精髓,只用了小三章把Java整體的脈絡過了一遍,強烈推薦用來復習。

下一步我的計划是jdk的concurrent包以及Java specification的並發部分,並發理解,除了基礎概念,就是要深刻領會各個應用場景下,有無並發問題以及如何寫出線程安全的代碼,個人覺得學習下無鎖的實現對理解有一定的幫助,但不用太費心思,到了Java Memory Model這一層基本就夠用了。

根據上面的闡述,我的路線圖可以總結如下:

學習心得 -- Java並發包的基礎概念

了解Java並發包有哪些工具以及相關基礎概念,有Java tutorial的concurrent章節和JCIP一書就足夠了。

JCIP一書的整體脈絡如下:

  1. 介紹多線程的利弊;
  2. 解釋線程安全是什么以及如何獲得線程安全;
  3. 從高頻的使用場景出發,介紹對象傳遞,類的設計等如何獲得線程安全;
  4. 從Java並發包出發,介紹高層的並發組件有啥以及相關原理;
  5. 介紹並發的一些弊端以及如何避免;
  6. 從Java並發包出發,介紹底層的並發組件以及原理;

總體看,該書有兩條主線,1 從高到低介紹Java並發包的一些重要組件和原理; 2 從並發場景出發,介紹如何利用這些組件來獲得線程安全。其中第二部分是這本書最大的特色,也是書名中有Practice的原因。

書中提到了幾個比較有意思的地方,

首先,到底什么是線程安全?

A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.
這個定義中,作者強調了正確地被多線程訪問, 同時要求沒有外加其他同步的手段。

那么,如何獲得線程安全?
Writing thread-safe code is, at its core, about managing access to state, and in particular to shared, mutable state.
書中將獲得線程安全總結為維護代碼的狀態,如果一個類是無狀態的(immutable),則自帶線程安全的屬性(函數式編程便是通過這種方式達到自帶的線程安全)。這些狀態大致可以理解為類中的非常量變量。 
通過這個可以了解到線程安全的本質,其實是共享變量,也就是狀態,有狀態的多線程訪問就需要同步機制來保證線程安全。

如何理解Java提供的用於處理並發的組件?
JDK提供的並發組件,大致可以分為兩類, 一類是預防為主,防止錯誤發生(race condition, visibility),大部分組件都是這類,還有一類是發生了錯誤但是能夠知道並及時重試(Atomic類提供的CAS),形象的例子有如 十字路口的信號燈,在流量小的時候,采用過多的預防措施反而會適得其反,例如白白的在大部分時間都沒有車的道路上等紅燈,這個時候適合采用犯錯(例如去掉紅綠燈,讓車自由行駛,遇到其他車的時候互相讓位即可)后解決的方法,能夠獲得最大的效率,在流量大的時候,紅綠燈的作用就能夠凸顯出來,其實規則的制定一定是在規模較大的時候才有意義,這也是預防的初衷。
類比到並發領域就是,在線程數量大,采用預防的措施比較好,這樣大部分線程就不會因為概率小的CAS重試浪費大量的cpu周期,在線程數量小的時候,CAS的意義就比較大,因為預防措施帶來的線程切換等的開銷可能大於CAS的等待,而且較少的線程也會讓CAS重試的等待時間變少。

以下是我根據這兩個資料概括出來的基礎概念,

理解這些基礎概念的核心,我覺得其實就是解決兩點問題:

  • Thread Interleaving,即多個線程讀寫共享變量造成的不一致問題;
  • Visibility,為了提高性能,處理器的每個執行單元其實都有緩存,這個雖然提高了某些數據的訪問性能但是卻給並發編程帶來了數據讀取的不一致性問題;

當然要更深入理解並發,還需要知道如何提升並發的性能,例如鎖的粒度如何把握?(經典的例子可以JDK的ConcurrentHashMap),底層一點的知識也得了解,例如CAS和Java Memory Model。

從高維視角了解並發

有了Java並發的基礎知識,接下來很適合閱讀七周七並發,我目前就在讀七周七並發,發現站在多種語言從范式的角度了解並發很有意思,原來Java提供的線程和鎖的機制其實相當於比較原始的工具了,其離底層最近。最近接觸了Scala,其使用了AKKA,則是一種高層的並發抽象。

七周七並發試圖從歷史的角度闡述作為鎖和線程的代表之Java的並發包的進化歷程,首先最早加入JDK的,其實是synchronized及其statement,但是發現缺少相關timeout和不能中斷等等功能,加入了可重入鎖,讀寫鎖等等,再后來又加入了各種線程安全的數據結構和高級同步機制。

接下來,七周七並發從函數式編程等等各種范式的角度闡述,除了線程和鎖,還有很多其他高層抽象可以更加方便的編寫並發代碼。

這本書對於充分理解並發,拓寬視野很有幫助,推薦大家閱讀。

從實現角度透徹理解並發

再深入下去的話,沒有比經典的JDK更合適的了,當然Google的Guava包也值得學習,從這些經典代碼了解各種組件的實現可以加深理解並更好的使用它們,但是作為應用端的程序員,倒是並不需要寫出這種較為底層的代碼(無鎖化)。

回顧這小一年的學習曲線,收獲良多,不過最后最值得強調的一點其實是,在做技術選擇的時候,並發只是工具箱中的一種手段,學習它只是為了能夠靈活運用,設計的首要選擇依然是在當時情境下的最簡化,能不用並發就不要用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM