Java并發(fā)編程總結(jié)

互聯(lián)網(wǎng)的快速發(fā)展,Java開(kāi)發(fā)的過(guò)程或多或少會(huì)需要進(jìn)行并發(fā)編程审洞,也會(huì)遇到一些并發(fā)編程帶來(lái)的各種bug。下面從并發(fā)編程的理論待讳、并發(fā)工具類(lèi)芒澜、并發(fā)設(shè)計(jì)模式、并發(fā)模型案例创淡,記錄一下自己的學(xué)習(xí)歷程痴晦。

1.并發(fā)編程理論

1.1 可見(jiàn)性、原子性辩昆、有序性

多核CPU的緩存與內(nèi)存的關(guān)系

并發(fā)編程的來(lái)源于緩存導(dǎo)致的可見(jiàn)性問(wèn)題阅酪,線(xiàn)程切換帶來(lái)的原子性問(wèn)題,編譯優(yōu)化帶來(lái)的有序性問(wèn)題汁针,也就是并發(fā)編程需要遵循的三個(gè)原則术辐。

可見(jiàn)性:一個(gè)線(xiàn)程對(duì)共享變量的修改,另外一個(gè)線(xiàn)程能夠立刻看到

原子性:一個(gè)或者多個(gè)操作在 CPU 執(zhí)行的過(guò)程中不被中斷的特性

有序性:程序按照代碼的先后順序執(zhí)行

1.2 Java內(nèi)存模型

Java語(yǔ)言規(guī)范引入了Java內(nèi)存模型施无,通過(guò)定義多項(xiàng)規(guī)則對(duì)編譯器和處理器進(jìn)行限制辉词,主要是針對(duì)可見(jiàn)性和有序性。主要是通過(guò)volatile猾骡、synchronized 和 final 三個(gè)關(guān)鍵字瑞躺,以及Happens-Before 規(guī)則。

(1)鎖兴想,鎖操作是具備happens-before關(guān)系的幢哨,解鎖操作happens-before之后對(duì)同一把鎖的加鎖操作。實(shí)際上嫂便,在解鎖的時(shí)候捞镰,JVM需要強(qiáng)制刷新緩存,使得當(dāng)前線(xiàn)程所修改的內(nèi)存對(duì)其他線(xiàn)程可見(jiàn)毙替。

(2)volatile字段岸售,volatile字段可以看成是一種不保證原子性的同步但保證可見(jiàn)性的特性,其性能往往是優(yōu)于鎖操作的厂画。但是凸丸,頻繁地訪(fǎng)問(wèn) volatile字段也會(huì)出現(xiàn)因?yàn)椴粩嗟貜?qiáng)制刷新緩存而影響程序的性能的問(wèn)題。

(3)final修飾符袱院,final修飾的實(shí)例字段則是涉及到新建對(duì)象的發(fā)布問(wèn)題屎慢。當(dāng)一個(gè)對(duì)象包含final修飾的實(shí)例字段時(shí)瞭稼,其他線(xiàn)程能夠看到已經(jīng)初始化的final實(shí)例字段,這是安全的腻惠。

Happpens-Before規(guī)則:

(1)程序次序規(guī)則:在一個(gè)線(xiàn)程內(nèi)弛姜,按照程序代碼順序,書(shū)寫(xiě)在前面的操作先行發(fā)生于書(shū)寫(xiě)在后面的操作妖枚。準(zhǔn)確地說(shuō),應(yīng)該是控制流順序而不是程序代碼順序苍在,因?yàn)橐紤]分支绝页、循環(huán)等結(jié)構(gòu)。

(2)管程鎖定規(guī)則:一個(gè)unlock操作先行發(fā)生于后面對(duì)同一個(gè)鎖的lock操作寂恬。這里必須強(qiáng)調(diào)的是同一個(gè)鎖续誉,而"后面"是指時(shí)間上的先后順序。

(3)volatile變量規(guī)則:對(duì)一個(gè)volatile變量的寫(xiě)操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作初肉,這里的"后面"同樣是指時(shí)間上的先后順序酷鸦。

(4)線(xiàn)程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法先行發(fā)生于此線(xiàn)程的每一個(gè)動(dòng)作。

(5)線(xiàn)程終止規(guī)則:線(xiàn)程中的所有操作都先行發(fā)生于對(duì)此線(xiàn)程的終止檢測(cè)牙咏,我們可以通過(guò)Thread.join()方法結(jié)束臼隔、Thread.isAlive()的返回值等手段檢測(cè)到線(xiàn)程已經(jīng)終止執(zhí)行。

(6)線(xiàn)程中斷規(guī)則:對(duì)線(xiàn)程interrupt()方法的調(diào)用先行發(fā)生于被中斷線(xiàn)程的代碼檢測(cè)到中斷事件的發(fā)生妄壶,可以通過(guò)Thread.interrupted()方法檢測(cè)到是否有中斷發(fā)生摔握。

(7)對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行發(fā)生于它的finalize()方法的開(kāi)始。

1.3 互斥鎖

互斥:同一個(gè)時(shí)刻只有一個(gè)線(xiàn)程在運(yùn)行丁寄。鎖是一種通用的技術(shù)方案,Java 語(yǔ)言提供的 synchronized 關(guān)鍵字,就是鎖的一種實(shí)現(xiàn)坚弱。synchronized 關(guān)鍵字可以用來(lái)修飾方法酒奶,也可以用來(lái)修飾代碼塊。

class X {

? // 修飾非靜態(tài)方法

? synchronized void foo() {

? ? // 臨界區(qū)

? }

? // 修飾靜態(tài)方法

? synchronized static void bar() {

? ? // 臨界區(qū)

? }

? // 修飾代碼塊

? Object obj = new Object()屑埋;

? void baz() {

? ? synchronized(obj) {

? ? ? // 臨界區(qū)

? ? }

? }

}?

當(dāng)修飾靜態(tài)方法的時(shí)候豪筝,鎖定的是當(dāng)前類(lèi)的 Class 對(duì)象;當(dāng)修飾非靜態(tài)方法的時(shí)候雀彼,鎖定的是當(dāng)前實(shí)例對(duì)象 this壤蚜。鎖的本質(zhì)是在鎖定對(duì)象的頭部字段寫(xiě)入鎖定狀態(tài)和線(xiàn)程信息,所以鎖定的對(duì)象需要是一個(gè)不變的對(duì)象徊哑。

當(dāng)用一把鎖鎖住多個(gè)資源袜刷,性能太差,會(huì)造成幾個(gè)操作都是串行莺丑。所以可以用多把鎖分別鎖不同的資源著蟹,不同的操作可以并行操作墩蔓。用不同的鎖對(duì)受保護(hù)資源進(jìn)行精細(xì)化管理,能夠提升性能萧豆。這種鎖還有個(gè)名字奸披,叫細(xì)粒度鎖。當(dāng)然這樣又會(huì)造成死鎖的情況出現(xiàn)涮雷。要避免死鎖就需要分析死鎖發(fā)生的條件阵面,有個(gè)叫 Coffman 的牛人早就總結(jié)過(guò)了,只有以下這四個(gè)條件都發(fā)生時(shí)才會(huì)出現(xiàn)死鎖:

(1)互斥洪鸭,共享資源 X 和 Y 只能被一個(gè)線(xiàn)程占用样刷;

(2)占有且等待,線(xiàn)程 T1 已經(jīng)取得共享資源 X览爵,在等待共享資源 Y 的時(shí)候置鼻,不釋放共享資源 X;

(3)不可搶占蜓竹,其他線(xiàn)程不能強(qiáng)行搶占線(xiàn)程 T1 占有的資源箕母;

(4)循環(huán)等待,線(xiàn)程 T1 等待線(xiàn)程 T2 占有的資源俱济,線(xiàn)程 T2 等待線(xiàn)程 T1 占有的資源嘶是,就是循環(huán)等待。

1.4?用“等待-通知”機(jī)制優(yōu)化循環(huán)等待

在 Java 語(yǔ)言里姨蝴,等待 - 通知機(jī)制可以有多種實(shí)現(xiàn)方式俊啼,比如 Java 語(yǔ)言?xún)?nèi)置的 synchronized 配合 wait()、notify()左医、notifyAll() 這三個(gè)方法就能輕松實(shí)現(xiàn)授帕。如果 synchronized 鎖定的是 this,那么對(duì)應(yīng)的一定是 this.wait()浮梢、this.notify()跛十、this.notifyAll();如果 synchronized 鎖定的是 target秕硝,那么對(duì)應(yīng)的一定是 target.wait()芥映、target.notify()、target.notifyAll() 远豺。notify() 是會(huì)隨機(jī)地通知等待隊(duì)列中的一個(gè)線(xiàn)程奈偏,而 notifyAll() 會(huì)通知等待隊(duì)列中的所有線(xiàn)程。有個(gè)阿姆達(dá)爾(Amdahl)定律躯护,代表了處理器并行運(yùn)算之后效率提升的能力惊来,它正好可以解決這個(gè)問(wèn)題,具體公式如下:

公式里的 n 可以理解為 CPU 的核數(shù)棺滞,p 可以理解為并行百分比裁蚁。

1.5 Java線(xiàn)程


Java線(xiàn)程狀態(tài)轉(zhuǎn)換圖

線(xiàn)程在sleep期間被打斷了矢渊,拋出一個(gè)InterruptedException異常,try catch捕捉此異常枉证,應(yīng)該重置一下中斷標(biāo)示矮男,因?yàn)閽伋霎惓:螅袛鄻?biāo)示會(huì)自動(dòng)清除掉室谚!

Thread th = Thread.currentThread();

while(true) {

??if(th.isInterrupted()) {

????break;

??}

??// 省略業(yè)務(wù)代碼無(wú)數(shù)

??try {

????Thread.sleep(100);

??}catch (InterruptedException e){

????Thread.currentThread().interrupt();

????e.printStackTrace();

??}

}

CPU 密集型計(jì)算:理論上“線(xiàn)程的數(shù)量 =CPU 核數(shù)”就是最合適的毡鉴。不過(guò)在工程上,線(xiàn)程的數(shù)量一般會(huì)設(shè)置為“CPU 核數(shù) +1”秒赤,這樣的話(huà)眨补,當(dāng)線(xiàn)程因?yàn)榕紶柕膬?nèi)存頁(yè)失效或其他原因?qū)е伦枞麜r(shí),這個(gè)額外的線(xiàn)程可以頂上倒脓,從而保證 CPU 的利用率。

?I/O 密集型:最佳線(xiàn)程數(shù) =CPU 核數(shù) * [ 1 +(I/O 耗時(shí) / CPU 耗時(shí))]

Java不支持尾遞歸含思,盡量不要使用遞歸崎弃。

2.并發(fā)工具類(lèi)


Java并發(fā)編程基礎(chǔ)類(lèi)

3.并發(fā)設(shè)計(jì)模式




Thread結(jié)構(gòu)圖

每個(gè)Thread線(xiàn)程內(nèi)部都有一個(gè)Map,Map里面存儲(chǔ)線(xiàn)程本地對(duì)象(key)和線(xiàn)程的變量副本(value)含潘,但是饲做,Thread內(nèi)部的Map是由ThreadLocal維護(hù)的,由ThreadLocal負(fù)責(zé)向map獲取和設(shè)置線(xiàn)程的變量值遏弱。

4.并發(fā)編程案例

4.1?高性能限流器Guava RateLimiter

令牌桶算法是定時(shí)向令牌桶發(fā)送令牌盆均,請(qǐng)求能夠從令牌桶中拿到令牌,然后才能通過(guò)限流器漱逸;而漏桶算法里泪姨,請(qǐng)求就像水一樣注入漏桶,漏桶會(huì)按照一定的速率自動(dòng)將水漏掉饰抒,只有漏桶里還能注入水的時(shí)候肮砾,請(qǐng)求才能通過(guò)限流器。Guava RateLimiter采用令牌桶算法袋坑。

4.2?高性能網(wǎng)絡(luò)應(yīng)用框架Netty

Netty 是一個(gè)款優(yōu)秀的網(wǎng)絡(luò)編程框架仗处,性能非常好,為了實(shí)現(xiàn)高性能的目標(biāo)枣宫,Netty 做了很多優(yōu)化婆誓,例如優(yōu)化了 ByteBuffer、支持零拷貝等等也颤,和并發(fā)編程相關(guān)的就是它的線(xiàn)程模型洋幻。

Netty線(xiàn)程模型

4.3?高性能隊(duì)列Disruptor

Disruptor 是一款高性能的有界內(nèi)存隊(duì)列,基于Disruptor開(kāi)發(fā)的系統(tǒng)單線(xiàn)程能支撐每秒600萬(wàn)訂單歇拆。原因如下:

(1)內(nèi)存分配更加合理鞋屈,使用 RingBuffer 數(shù)據(jù)結(jié)構(gòu)范咨,數(shù)組元素在初始化時(shí)一次性全部創(chuàng)建,提升緩存命中率厂庇;對(duì)象循環(huán)利用渠啊,避免頻繁 GC。

(2)能夠避免偽共享权旷,提升緩存利用率替蛉。

(3)采用無(wú)鎖算法,避免頻繁加鎖拄氯、解鎖的性能消耗躲查。

(4)支持批量消費(fèi),消費(fèi)者可以無(wú)鎖方式消費(fèi)多個(gè)消息译柏。

Disruptor詳細(xì)介紹:高性能隊(duì)列Disruptor?很詳細(xì)镣煮。

4.4 高性能數(shù)據(jù)庫(kù)連接池HiKariCP


數(shù)據(jù)庫(kù)連接池

HiKariCP 號(hào)稱(chēng)是業(yè)界跑得最快的數(shù)據(jù)庫(kù)連接池,主要是有兩個(gè)特殊的數(shù)據(jù)結(jié)構(gòu):FastList鄙麦、ConcurrentBag典唇。

1.FastList將remove(Object element)?方法的查找順序變成了逆序查找;get(int index)?方法沒(méi)有對(duì) index 參數(shù)進(jìn)行越界檢查胯府,HiKariCP 能保證不會(huì)越界

2.ConcurrentBag 通過(guò) ThreadLocal 做一次預(yù)分配介衔,避免直接競(jìng)爭(zhēng)共享資源,非常適合池化資源的分配

4.5?Actor模型

Actor模型基于消息機(jī)制骂因,可以實(shí)現(xiàn)并發(fā)編程和分布式編程炎咖。

4.6?軟件事務(wù)內(nèi)存

MVCC(全稱(chēng)是 Multi-Version Concurrency Control),也就是多版本并發(fā)控制寒波,具體代表是:Multiverse

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乘盼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子俄烁,更是在濱河造成了極大的恐慌蹦肴,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猴娩,死亡現(xiàn)場(chǎng)離奇詭異阴幌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)卷中,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)矛双,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蟆豫,你說(shuō)我怎么就攤上這事议忽。” “怎么了十减?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵栈幸,是天一觀(guān)的道長(zhǎng)愤估。 經(jīng)常有香客問(wèn)我,道長(zhǎng)速址,這世上最難降的妖魔是什么玩焰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮芍锚,結(jié)果婚禮上昔园,老公的妹妹穿的比我還像新娘。我一直安慰自己并炮,他們只是感情好默刚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著逃魄,像睡著了一般荤西。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伍俘,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天皂冰,我揣著相機(jī)與錄音,去河邊找鬼养篓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赂蕴,可吹牛的內(nèi)容都是我干的柳弄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼概说,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碧注!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起糖赔,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤萍丐,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后放典,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逝变,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年奋构,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壳影。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弥臼,死狀恐怖宴咧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情径缅,我是刑警寧澤掺栅,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布烙肺,位于F島的核電站,受9級(jí)特大地震影響氧卧,放射性物質(zhì)發(fā)生泄漏桃笙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一假抄、第九天 我趴在偏房一處隱蔽的房頂上張望怎栽。 院中可真熱鬧,春花似錦宿饱、人聲如沸熏瞄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)强饮。三九已至,卻和暖如春为黎,著一層夾襖步出監(jiān)牢的瞬間邮丰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工铭乾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剪廉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓炕檩,卻偏偏與公主長(zhǎng)得像斗蒋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笛质,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容