一年前由于工作需要從微軟技術(shù)棧入坑Java,并陸陸續(xù)續(xù)做了一個(gè)Java后臺(tái)項(xiàng)目,目前在搞Scala+Java混合的后臺(tái)開(kāi)發(fā),一直覺(jué)得并發(fā)編程是所有后臺(tái)工程師的基本功坪哄,所以也學(xué)習(xí)了小一年Java的并發(fā)工具,對(duì)整體的并發(fā)理解乃至分布式都有一定的提高势篡,所以想和大家分享一下翩肌。
我的學(xué)習(xí)路線
首先說(shuō)說(shuō)學(xué)習(xí)路線,我一開(kāi)始是直接上手JCIP(Java Concurrency in Practice)禁悠,發(fā)現(xiàn)不是很好懂念祭,把握不了那本書(shū)的主線,所以思索著從國(guó)內(nèi)的作者開(kāi)始先碍侦,所以便讀了下方騰飛的《Java并發(fā)編程的藝術(shù)》的粱坤,雖然豆瓣上的評(píng)價(jià)一般,但是對(duì)于構(gòu)建Java并發(fā)的整體映像還是有所提高的瓷产,至少我知道了有哪些東西要深入學(xué)習(xí)站玄。接著我想加強(qiáng)下并發(fā)的理論,繼續(xù)讀了The Art of Multiprocessor Programming濒旦,這本書(shū)比較艱澀株旷,不是很好懂,但是過(guò)一遍還是好處多多疤估,建議初學(xué)者了解下概念的過(guò)過(guò)灾常,后期可以再來(lái)翻看。有了以上兩步的支持铃拇,接下來(lái)就又開(kāi)始啃JCIP了钞瀑,發(fā)現(xiàn)比以前有了不同的感覺(jué),我能比較輕松的跟上書(shū)的脈絡(luò)慷荔,知道書(shū)的整體框架雕什,讀起來(lái)不那么費(fèi)勁了,這本書(shū)號(hào)稱Java并發(fā)編程的圣經(jīng)显晶,確實(shí)可以看出作者有很豐富的并發(fā)實(shí)踐經(jīng)驗(yàn)贷岸。再后來(lái)我過(guò)了一遍Oracle官網(wǎng)上的Java Tutorial關(guān)于并發(fā)的那一章,發(fā)現(xiàn)講的也不錯(cuò)磷雇,對(duì)于了解基礎(chǔ)庫(kù)有哪些組件幫助挺大偿警。
到了這一步,接下來(lái)怎么繼續(xù)提高呢唯笙?我發(fā)現(xiàn)了一本很有趣的書(shū)螟蒸,《七周七并發(fā)模型》,之前的視野一直是在Java并發(fā)編程的工具包中深入了解崩掘,感覺(jué)七嫌,應(yīng)該跳出來(lái),從模型的角度看看各個(gè)語(yǔ)言的并發(fā)實(shí)現(xiàn)的原理苞慢,我目前正處于這一步诵原,發(fā)現(xiàn)很有意思,第一章講Java的線程和鎖這個(gè)模型就感覺(jué)很精髓挽放,只用了小三章把Java整體的脈絡(luò)過(guò)了一遍绍赛,強(qiáng)烈推薦用來(lái)復(fù)習(xí)。
下一步我的計(jì)劃是jdk的concurrent包以及Java specification的并發(fā)部分辑畦,并發(fā)理解惹资,除了基礎(chǔ)概念,就是要深刻領(lǐng)會(huì)各個(gè)應(yīng)用場(chǎng)景下航闺,有無(wú)并發(fā)問(wèn)題以及如何寫(xiě)出線程安全的代碼褪测,個(gè)人覺(jué)得學(xué)習(xí)下無(wú)鎖的實(shí)現(xiàn)對(duì)理解有一定的幫助,但不用太費(fèi)心思潦刃,到了Java Memory Model這一層基本就夠用了侮措。
根據(jù)上面的闡述,我的路線圖可以總結(jié)如下:
學(xué)習(xí)心得 -- Java并發(fā)包的基礎(chǔ)概念
了解Java并發(fā)包有哪些工具以及相關(guān)基礎(chǔ)概念乖杠,有Java tutorial的concurrent章節(jié)和JCIP一書(shū)就足夠了分扎。
JCIP一書(shū)的整體脈絡(luò)如下:
介紹多線程的利弊;
解釋線程安全是什么以及如何獲得線程安全胧洒;
從高頻的使用場(chǎng)景出發(fā)畏吓,介紹對(duì)象傳遞墨状,類(lèi)的設(shè)計(jì)等如何獲得線程安全;
從Java并發(fā)包出發(fā)菲饼,介紹高層的并發(fā)組件有啥以及相關(guān)原理肾砂;
介紹并發(fā)的一些弊端以及如何避免;
從Java并發(fā)包出發(fā)宏悦,介紹底層的并發(fā)組件以及原理镐确;
總體看,該書(shū)有兩條主線饼煞,1 從高到低介紹Java并發(fā)包的一些重要組件和原理源葫; 2 從并發(fā)場(chǎng)景出發(fā),介紹如何利用這些組件來(lái)獲得線程安全砖瞧。其中第二部分是這本書(shū)最大的特色息堂,也是書(shū)名中有Practice的原因。
書(shū)中提到了幾個(gè)比較有意思的地方块促,
首先储矩,到底什么是線程安全?
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.
這個(gè)定義中褂乍,作者強(qiáng)調(diào)了正確地被多線程訪問(wèn)持隧, 同時(shí)要求沒(méi)有外加其他同步的手段。
那么逃片,如何獲得線程安全屡拨?
Writing thread-safe code is, at its core, about managing access to state, and in particular to shared, mutable state.
書(shū)中將獲得線程安全總結(jié)為維護(hù)代碼的狀態(tài),如果一個(gè)類(lèi)是無(wú)狀態(tài)的(immutable)褥实,則自帶線程安全的屬性(函數(shù)式編程便是通過(guò)這種方式達(dá)到自帶的線程安全)呀狼。這些狀態(tài)大致可以理解為類(lèi)中的非常量變量。
通過(guò)這個(gè)可以了解到線程安全的本質(zhì)损离,其實(shí)是共享變量哥艇,也就是狀態(tài),有狀態(tài)的多線程訪問(wèn)就需要同步機(jī)制來(lái)保證線程安全僻澎。
如何理解Java提供的用于處理并發(fā)的組件貌踏?
JDK提供的并發(fā)組件,大致可以分為兩類(lèi)窟勃, 一類(lèi)是預(yù)防為主祖乳,防止錯(cuò)誤發(fā)生(race condition, visibility),大部分組件都是這類(lèi)秉氧,還有一類(lèi)是發(fā)生了錯(cuò)誤但是能夠知道并及時(shí)重試(Atomic類(lèi)提供的CAS)眷昆,形象的例子有如 十字路口的信號(hào)燈,在流量小的時(shí)候,采用過(guò)多的預(yù)防措施反而會(huì)適得其反亚斋,例如白白的在大部分時(shí)間都沒(méi)有車(chē)的道路上等紅燈作媚,這個(gè)時(shí)候適合采用犯錯(cuò)(例如去掉紅綠燈,讓車(chē)自由行駛帅刊,遇到其他車(chē)的時(shí)候互相讓位即可)后解決的方法纸泡,能夠獲得最大的效率,在流量大的時(shí)候厚掷,紅綠燈的作用就能夠凸顯出來(lái)弟灼,其實(shí)規(guī)則的制定一定是在規(guī)模較大的時(shí)候才有意義级解,這也是預(yù)防的初衷冒黑。
類(lèi)比到并發(fā)領(lǐng)域就是,在線程數(shù)量大勤哗,采用預(yù)防的措施比較好抡爹,這樣大部分線程就不會(huì)因?yàn)楦怕市〉腃AS重試?yán)速M(fèi)大量的cpu周期,在線程數(shù)量小的時(shí)候芒划,CAS的意義就比較大举娩,因?yàn)轭A(yù)防措施帶來(lái)的線程切換等的開(kāi)銷(xiāo)可能大于CAS的等待饵溅,而且較少的線程也會(huì)讓CAS重試的等待時(shí)間變少。
以下是我根據(jù)這兩個(gè)資料概括出來(lái)的基礎(chǔ)概念,
理解這些基礎(chǔ)概念的核心页滚,我覺(jué)得其實(shí)就是解決兩點(diǎn)問(wèn)題:
Thread Interleaving,即多個(gè)線程讀寫(xiě)共享變量造成的不一致問(wèn)題徘郭;
Visibility吐辙,為了提高性能,處理器的每個(gè)執(zhí)行單元其實(shí)都有緩存疮鲫,這個(gè)雖然提高了某些數(shù)據(jù)的訪問(wèn)性能但是卻給并發(fā)編程帶來(lái)了數(shù)據(jù)讀取的不一致性問(wèn)題吆你;
當(dāng)然要更深入理解并發(fā),還需要知道如何提升并發(fā)的性能俊犯,例如鎖的粒度如何把握妇多?(經(jīng)典的例子可以JDK的ConcurrentHashMap),底層一點(diǎn)的知識(shí)也得了解燕侠,例如CAS和Java Memory Model者祖。
PS. 最近又看了《深入理解Java虛擬機(jī)》中的并發(fā)部分,發(fā)現(xiàn)理解的又有變化绢彤,重新整了下Java并發(fā)編程知識(shí)圖譜的2.0版本:
從高維視角了解并發(fā)
有了Java并發(fā)的基礎(chǔ)知識(shí)咸包,接下來(lái)很適合閱讀七周七并發(fā),我目前就在讀七周七并發(fā)杖虾,發(fā)現(xiàn)站在多種語(yǔ)言從范式的角度了解并發(fā)很有意思烂瘫,原來(lái)Java提供的線程和鎖的機(jī)制其實(shí)相當(dāng)于比較原始的工具了,其離底層最近。最近接觸了Scala坟比,其使用了AKKA芦鳍,則是一種高層的并發(fā)抽象。
七周七并發(fā)試圖從歷史的角度闡述作為鎖和線程的代表之Java的并發(fā)包的進(jìn)化歷程葛账,首先最早加入JDK的柠衅,其實(shí)是synchronized及其statement,但是發(fā)現(xiàn)缺少相關(guān)timeout和不能中斷等等功能籍琳,加入了可重入鎖菲宴,讀寫(xiě)鎖等等,再后來(lái)又加入了各種線程安全的數(shù)據(jù)結(jié)構(gòu)和高級(jí)同步機(jī)制趋急。
接下來(lái)喝峦,七周七并發(fā)從函數(shù)式編程等等各種范式的角度闡述,除了線程和鎖呜达,還有很多其他高層抽象可以更加方便的編寫(xiě)并發(fā)代碼谣蠢。
這本書(shū)對(duì)于充分理解并發(fā),拓寬視野很有幫助查近,推薦大家閱讀眉踱。
從實(shí)現(xiàn)角度透徹理解并發(fā)
再深入下去的話,沒(méi)有比經(jīng)典的JDK更合適的了霜威,當(dāng)然Google的Guava包也值得學(xué)習(xí)谈喳,從這些經(jīng)典代碼了解各種組件的實(shí)現(xiàn)可以加深理解并更好的使用它們,但是作為應(yīng)用端的程序員戈泼,倒是并不需要寫(xiě)出這種較為底層的代碼(無(wú)鎖化)婿禽。
回顧這小一年的學(xué)習(xí)曲線,收獲良多矮冬,不過(guò)最后最值得強(qiáng)調(diào)的一點(diǎn)其實(shí)是谈宛,在做技術(shù)選擇的時(shí)候,并發(fā)只是工具箱中的一種手段胎署,學(xué)習(xí)它只是為了能夠靈活運(yùn)用吆录,設(shè)計(jì)的首要選擇依然是在當(dāng)時(shí)情境下的最簡(jiǎn)化,能不用并發(fā)就不要用琼牧。《 Java學(xué)習(xí)恢筝、面試;文檔巨坊、視頻資源免費(fèi)獲取》