JMM的介紹
在上一篇文章中總結(jié)了線程的狀態(tài)轉(zhuǎn)換和一些基本操作,對(duì)多線程已經(jīng)有一點(diǎn)基本的認(rèn)識(shí)了沈自,如果多線程編程只有這么簡(jiǎn)單毡代,那我們就不必費(fèi)勁周折的去學(xué)習(xí)它了。
在多線程中稍微不注意就會(huì)出現(xiàn)線程安全問(wèn)題间校,那么什么是線程安全問(wèn)題矾克?我的認(rèn)識(shí)是,在多線程下代碼執(zhí)行的結(jié)果與預(yù)期正確的結(jié)果不一致憔足,該代碼就是線程不安全的胁附,否則則是線程安全的。雖然這種回答似乎不能獲取什么內(nèi)容滓彰,可以google下控妻。在<<深入理解Java虛擬機(jī)>>中看到的定義。
原文如下:
當(dāng)多個(gè)線程訪問(wèn)同一個(gè)對(duì)象時(shí)揭绑,如果不用考慮這些線程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交替運(yùn)行弓候,也不需要進(jìn)行額外的同步,或者在調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作洗做,調(diào)用這個(gè)對(duì)象的行為都可以獲取正確的結(jié)果弓叛,那這個(gè)對(duì)象是線程安全的。
關(guān)于定義的理解這是一個(gè)仁者見(jiàn)仁智者見(jiàn)智的事情诚纸。
出現(xiàn)線程安全的問(wèn)題一般是因?yàn)橹鲀?nèi)存和工作內(nèi)存數(shù)據(jù)不一致性和重排序?qū)е碌淖辏鉀Q線程安全的問(wèn)題最重要的就是理解這兩種問(wèn)題是怎么來(lái)的,那么畦徘,理解它們的核心在于理解java內(nèi)存模型(JMM)毕籽。
在多線程條件下,多個(gè)線程肯定會(huì)相互協(xié)作完成一件事情井辆,一般來(lái)說(shuō)就會(huì)涉及到多個(gè)線程間相互通信告知彼此的狀態(tài)以及當(dāng)前的執(zhí)行結(jié)果等关筒,另外,為了性能優(yōu)化杯缺,還會(huì)涉及到編譯器指令重排序和處理器指令重排序蒸播。下面會(huì)一一來(lái)聊聊這些知識(shí)。
內(nèi)存模型抽象結(jié)構(gòu)
線程間協(xié)作通信可以類(lèi)比人與人之間的協(xié)作的方式,在現(xiàn)實(shí)生活中袍榆,之前網(wǎng)上有個(gè)流行語(yǔ)“你媽喊你回家吃飯了”胀屿,就以這個(gè)生活場(chǎng)景為例。小明在外面玩耍包雀,小明媽媽在家里做飯宿崭,做晚飯后準(zhǔn)備叫小明回家吃飯,那么就存在兩種方式:
小明媽媽要去上班了十分緊急這個(gè)時(shí)候手機(jī)又沒(méi)有電了才写,于是就在桌子上貼了一張紙條“飯做好了葡兑,放在...”小明回家后看到紙條如愿吃到媽媽做的飯菜,那么赞草,如果將小明媽媽和小明作為兩個(gè)線程讹堤,那么這張紙條就是這兩個(gè)線程間通信的共享變量,通過(guò)讀寫(xiě)共享變量實(shí)現(xiàn)兩個(gè)線程間協(xié)作房资;
還有一種方式就是蜕劝,媽媽的手機(jī)還有電,媽媽在趕去坐公交的路上給小明打了個(gè)電話轰异,這種方式就是通知機(jī)制來(lái)完成協(xié)作岖沛。同樣,可以引申到線程間通信機(jī)制搭独。
通過(guò)上面這個(gè)例子婴削,應(yīng)該有些認(rèn)識(shí)。在并發(fā)編程中主要需要解決兩個(gè)問(wèn)題:1. 線程之間如何通信牙肝;2.線程之間如何完成同步(這里的線程指的是并發(fā)執(zhí)行的活動(dòng)實(shí)體)唉俗。通信是指線程之間以何種機(jī)制來(lái)交換信息,主要有兩種:共享內(nèi)存和消息傳遞配椭。
這里虫溜,可以分別類(lèi)比上面的兩個(gè)舉例。java內(nèi)存模型是共享內(nèi)存的并發(fā)模型股缸,線程之間主要通過(guò)讀-寫(xiě)共享變量來(lái)完成隱式通信衡楞。如果程序員不能理解Java的共享內(nèi)存模型在編寫(xiě)并發(fā)程序時(shí)一定會(huì)遇到各種各樣關(guān)于內(nèi)存可見(jiàn)性的問(wèn)題。
1.哪些是共享變量
在java程序中所有實(shí)例域敦姻,靜態(tài)域和數(shù)組元素都是放在堆內(nèi)存中(所有線程均可訪問(wèn)到瘾境,是可以共享的),而局部變量镰惦,方法定義參數(shù)和異常處理器參數(shù)不會(huì)在線程間共享迷守。共享數(shù)據(jù)會(huì)出現(xiàn)線程安全的問(wèn)題,而非共享數(shù)據(jù)不會(huì)出現(xiàn)線程安全的問(wèn)題旺入。關(guān)于JVM運(yùn)行時(shí)內(nèi)存區(qū)域在后面的文章會(huì)講到兑凿。
2.JMM抽象結(jié)構(gòu)模型
我們知道CPU的處理速度和主存的讀寫(xiě)速度不是一個(gè)量級(jí)的凯力,為了平衡這種巨大的差距,每個(gè)CPU都會(huì)有緩存礼华。因此沮协,共享變量會(huì)先放在主存中,每個(gè)線程都有屬于自己的工作內(nèi)存卓嫂,并且會(huì)把位于主存中的共享變量拷貝到自己的工作內(nèi)存,之后的讀寫(xiě)操作均使用位于工作內(nèi)存的變量副本聘殖,并在某個(gè)時(shí)刻將工作內(nèi)存的變量副本寫(xiě)回到主存中去晨雳。JMM就從抽象層次定義了這種方式,并且JMM決定了一個(gè)線程對(duì)共享變量的寫(xiě)入何時(shí)對(duì)其他線程是可見(jiàn)的奸腺。
如圖為JMM抽象示意圖餐禁,線程A和線程B之間要完成通信的話,要經(jīng)歷如下兩步:
線程A從主內(nèi)存中將共享變量讀入線程A的工作內(nèi)存后并進(jìn)行操作突照,之后將數(shù)據(jù)重新寫(xiě)回到主內(nèi)存中帮非;
線程B從主存中讀取最新的共享變量
從橫向去看看,線程A和線程B就好像通過(guò)共享變量在進(jìn)行隱式通信讹蘑。這其中有很有意思的問(wèn)題末盔,如果線程A更新后數(shù)據(jù)并沒(méi)有及時(shí)寫(xiě)回到主存,而此時(shí)線程B讀到的是過(guò)期的數(shù)據(jù)座慰,這就出現(xiàn)了“臟讀”現(xiàn)象陨舱。
可以通過(guò)同步機(jī)制(控制不同線程間操作發(fā)生的相對(duì)順序)來(lái)解決或者通過(guò)volatile關(guān)鍵字使得每次volatile變量都能夠強(qiáng)制刷新到主存,從而對(duì)每個(gè)線程都是可見(jiàn)的版仔。
3. 重排序
一個(gè)好的內(nèi)存模型實(shí)際上會(huì)放松對(duì)處理器和編譯器規(guī)則的束縛游盲,也就是說(shuō)軟件技術(shù)和硬件技術(shù)都為同一個(gè)目標(biāo)而進(jìn)行奮斗:在不改變程序執(zhí)行結(jié)果的前提下,盡可能提高并行度蛮粮。JMM對(duì)底層盡量減少約束益缎,使其能夠發(fā)揮自身優(yōu)勢(shì)。因此然想,在執(zhí)行程序時(shí)莺奔,為了提高性能,編譯器和處理器常常會(huì)對(duì)指令進(jìn)行重排序又沾。一般重排序可以分為如下三種:
編譯器優(yōu)化的重排序弊仪。編譯器在不改變單線程程序語(yǔ)義的前提下,可以重新安排語(yǔ)句的執(zhí)行順序杖刷;
指令級(jí)并行的重排序±現(xiàn)代處理器采用了指令級(jí)并行技術(shù)來(lái)將多條指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴(lài)性滑燃,處理器可以改變語(yǔ)句對(duì)應(yīng)機(jī)器指令的執(zhí)行順序役听;
內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫(xiě)緩沖區(qū),這使得加載和存儲(chǔ)操作看上去可能是在亂序執(zhí)行的典予。
如圖甜滨,1屬于編譯器重排序,而2和3統(tǒng)稱(chēng)為處理器重排序瘤袖。這些重排序會(huì)導(dǎo)致線程安全的問(wèn)題衣摩,一個(gè)很經(jīng)典的例子就是DCL問(wèn)題,這個(gè)在以后的文章中會(huì)具體去聊捂敌。針對(duì)編譯器重排序艾扮,JMM的編譯器重排序規(guī)則會(huì)禁止一些特定類(lèi)型的編譯器重排序;針對(duì)處理器重排序占婉,編譯器在生成指令序列的時(shí)候會(huì)通過(guò)插入內(nèi)存屏障指令來(lái)禁止某些特殊的處理器重排序泡嘴。
那么什么情況下,不能進(jìn)行重排序了逆济?下面就來(lái)說(shuō)說(shuō)數(shù)據(jù)依賴(lài)性酌予。有如下代碼:
double?pi =?3.14?//A
double?r =?1.0?//B
double?area = pi * r * r?//C
這是一個(gè)計(jì)算圓面積的代碼,由于A,B之間沒(méi)有任何關(guān)系奖慌,對(duì)最終結(jié)果也不會(huì)存在關(guān)系抛虫,它們之間執(zhí)行順序可以重排序。因此可以執(zhí)行順序可以是A->B->C或者B->A->C執(zhí)行最終結(jié)果都是3.14简僧,即A和B之間沒(méi)有數(shù)據(jù)依賴(lài)性莱褒。
具體的定義為:如果兩個(gè)操作訪問(wèn)同一個(gè)變量,且這兩個(gè)操作有一個(gè)為寫(xiě)操作涎劈,此時(shí)這兩個(gè)操作就存在數(shù)據(jù)依賴(lài)性這里就存在三種情況:1. 讀后寫(xiě)广凸;2.寫(xiě)后寫(xiě);3. 寫(xiě)后讀蛛枚,者三種操作都是存在數(shù)據(jù)依賴(lài)性的谅海,如果重排序會(huì)對(duì)最終執(zhí)行結(jié)果會(huì)存在影響。
編譯器和處理器在重排序時(shí)蹦浦,會(huì)遵守?cái)?shù)據(jù)依賴(lài)性扭吁,編譯器和處理器不會(huì)改變存在數(shù)據(jù)依賴(lài)性關(guān)系的兩個(gè)操作的執(zhí)行順序。
另外盲镶,還有一個(gè)比較有意思的就是as-if-serial語(yǔ)義侥袜。
as-if-serial
as-if-serial語(yǔ)義的意思是:不管怎么重排序(編譯器和處理器為了提供并行度),(單線程)程序的執(zhí)行結(jié)果不能被改變溉贿。編譯器枫吧,runtime和處理器都必須遵守as-if-serial語(yǔ)義。
as-if-serial語(yǔ)義把單線程程序保護(hù)了起來(lái)宇色,遵守as-if-serial語(yǔ)義的編譯器九杂,runtime和處理器共同為編寫(xiě)單線程程序的程序員創(chuàng)建了一個(gè)幻覺(jué):?jiǎn)尉€程程序是按程序的順序來(lái)執(zhí)行的颁湖。
比如上面計(jì)算圓面積的代碼,在單線程中例隆,會(huì)讓人感覺(jué)代碼是一行一行順序執(zhí)行上甥捺,實(shí)際上A,B兩行不存在數(shù)據(jù)依賴(lài)性可能會(huì)進(jìn)行重排序,即A镀层,B不是順序執(zhí)行的镰禾。as-if-serial語(yǔ)義使程序員不必?fù)?dān)心單線程中重排序的問(wèn)題干擾他們,也無(wú)需擔(dān)心內(nèi)存可見(jiàn)性問(wèn)題唱逢。
4. happens-before規(guī)則
上面的內(nèi)容講述了重排序原則羡微,一會(huì)是編譯器重排序一會(huì)是處理器重排序,如果讓程序員再去了解這些底層的實(shí)現(xiàn)以及具體規(guī)則惶我,那么程序員的負(fù)擔(dān)就太重了,嚴(yán)重影響了并發(fā)編程的效率博投。
因此绸贡,JMM為程序員在上層提供了六條規(guī)則,這樣我們就可以根據(jù)規(guī)則去推論跨線程的內(nèi)存可見(jiàn)性問(wèn)題毅哗,而不用再去理解底層重排序的規(guī)則听怕。下面以?xún)蓚€(gè)方面來(lái)說(shuō)。
4.1 happens-before定義
happens-before的概念最初由Leslie Lamport在其一篇影響深遠(yuǎn)的論文(《Time虑绵,Clocks and the Ordering of Events in a Distributed System》)中提出尿瞭,有興趣的可以google一下。JSR-133使用happens-before的概念來(lái)指定兩個(gè)操作之間的執(zhí)行順序翅睛。由于這兩個(gè)操作可以在一個(gè)線程之內(nèi)声搁,也可以是在不同線程之間。
因此捕发,JMM可以通過(guò)happens-before關(guān)系向程序員提供跨線程的內(nèi)存可見(jiàn)性保證(如果A線程的寫(xiě)操作a與B線程的讀操作b之間存在happens-before關(guān)系疏旨,盡管a操作和b操作在不同的線程中執(zhí)行,但JMM向程序員保證a操作將對(duì)b操作可見(jiàn))扎酷。
具體的定義為:
1)如果一個(gè)操作happens-before另一個(gè)操作檐涝,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見(jiàn),而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前法挨。
2)兩個(gè)操作之間存在happens-before關(guān)系谁榜,并不意味著Java平臺(tái)的具體實(shí)現(xiàn)必須要按照happens-before關(guān)系指定的順序來(lái)執(zhí)行。如果重排序之后的執(zhí)行結(jié)果凡纳,與按happens-before關(guān)系來(lái)執(zhí)行的結(jié)果一致窃植,那么這種重排序并不非法(也就是說(shuō),JMM允許這種重排序)荐糜。
上面的(1)是JMM對(duì)程序員的承諾撕瞧。從程序員的角度來(lái)說(shuō)陵叽,可以這樣理解happens-before關(guān)系:如果A happens-before B,那么Java內(nèi)存模型將向程序員保證——A操作的結(jié)果將對(duì)B可見(jiàn)丛版,且A的執(zhí)行順序排在B之前巩掺。注意,這只是Java內(nèi)存模型向程序員做出的保證页畦!
上面的(2)是JMM對(duì)編譯器和處理器重排序的約束原則胖替。正如前面所言,JMM其實(shí)是在遵循一個(gè)基本原則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序)豫缨,編譯器和處理器怎么優(yōu)化都行独令。JMM這么做的原因是:程序員對(duì)于這兩個(gè)操作是否真的被重排序并不關(guān)心,程序員關(guān)心的是程序執(zhí)行時(shí)的語(yǔ)義不能被改變(即執(zhí)行結(jié)果不能被改變)好芭。
因此燃箭,happens-before關(guān)系本質(zhì)上和as-if-serial語(yǔ)義是一回事。
下面來(lái)比較一下as-if-serial和happens-before:
as-if-serial ? VS ? happens-before
as-if-serial語(yǔ)義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變舍败,happens-before關(guān)系保證正確同步的多線程程序的執(zhí)行結(jié)果不被改變招狸。
as-if-serial語(yǔ)義給編寫(xiě)單線程程序的程序員創(chuàng)造了一個(gè)幻境:?jiǎn)尉€程程序是按程序的順序來(lái)執(zhí)行的。happens-before關(guān)系給編寫(xiě)正確同步的多線程程序的程序員創(chuàng)造了一個(gè)幻境:正確同步的多線程程序是按happens-before指定的順序來(lái)執(zhí)行的邻薯。
as-if-serial語(yǔ)義和happens-before這么做的目的裙戏,都是為了在不改變程序執(zhí)行結(jié)果的前提下,盡可能地提高程序執(zhí)行的并行度厕诡。
4.2 具體規(guī)則
具體的一共有八項(xiàng)規(guī)則:
程序順序規(guī)則:一個(gè)線程中的每個(gè)操作累榜,happens-before于該線程中的任意后續(xù)操作。
監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖灵嫌,happens-before于隨后對(duì)這個(gè)鎖的加鎖壹罚。
volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫(xiě),happens-before于任意后續(xù)對(duì)這個(gè)volatile域的讀寿羞。
傳遞性:如果A happens-before B渔嚷,且B happens-before C,那么A happens-before C稠曼。
start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動(dòng)線程B)形病,那么A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回霞幅,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回漠吻。
程序中斷規(guī)則:對(duì)線程interrupted()方法的調(diào)用先行于被中斷線程的代碼檢測(cè)到中斷時(shí)間的發(fā)生。
對(duì)象finalize規(guī)則:一個(gè)對(duì)象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行于發(fā)生它的finalize()方法的開(kāi)始司恳。
下面以一個(gè)具體的例子來(lái)講下如何使用這些規(guī)則進(jìn)行推論:
依舊以上面計(jì)算圓面積的進(jìn)行描述途乃。利用程序順序規(guī)則(規(guī)則1)存在三個(gè)happens-before關(guān)系:1. A happens-before B;2. B happens-before C;3. A happens-before C扔傅。這里的第三個(gè)關(guān)系是利用傳遞性進(jìn)行推論的耍共。
A happens-before B,定義1要求A執(zhí)行結(jié)果對(duì)B可見(jiàn)烫饼,并且A操作的執(zhí)行順序在B操作之前,但與此同時(shí)利用定義中的第二條试读,A,B操作彼此不存在數(shù)據(jù)依賴(lài)性杠纵,兩個(gè)操作的執(zhí)行順序?qū)ψ罱K結(jié)果都不會(huì)產(chǎn)生影響,在不改變最終結(jié)果的前提下钩骇,允許A比藻,B兩個(gè)操作重排序,即happens-before關(guān)系并不代表了最終的執(zhí)行順序倘屹。
5. 總結(jié)
上面已經(jīng)聊了關(guān)于JMM的兩個(gè)方面:
JMM的抽象結(jié)構(gòu)(主內(nèi)存和線程工作內(nèi)存)银亲;
重排序以及happens-before規(guī)則。
接下來(lái)纽匙,我們來(lái)做一個(gè)總結(jié)务蝠。從兩個(gè)方面進(jìn)行考慮。
如果讓我們?cè)O(shè)計(jì)JMM應(yīng)該從哪些方面考慮烛缔,也就是說(shuō)JMM承擔(dān)哪些功能馏段;
happens-before與JMM的關(guān)系;
由于JMM力穗,多線程情況下可能會(huì)出現(xiàn)哪些問(wèn)題?
5.1 JMM的設(shè)計(jì)
JMM是語(yǔ)言級(jí)的內(nèi)存模型气嫁,在我的理解中JMM處于中間層当窗,包含了兩個(gè)方面:(1)內(nèi)存模型;(2)重排序以及happens-before規(guī)則寸宵。同時(shí)崖面,為了禁止特定類(lèi)型的重排序會(huì)對(duì)編譯器和處理器指令序列加以控制。
而上層會(huì)有基于JMM的關(guān)鍵字和J.U.C包下的一些具體類(lèi)用來(lái)方便程序員能夠迅速高效率的進(jìn)行并發(fā)編程梯影。站在JMM設(shè)計(jì)者的角度巫员,在設(shè)計(jì)JMM時(shí)需要考慮兩個(gè)關(guān)鍵因素:
程序員對(duì)內(nèi)存模型的使用
程序員希望內(nèi)存模型易于理解、易于編程甲棍。程序員希望基于一個(gè)強(qiáng)內(nèi)存模型來(lái)編寫(xiě)代碼简识。
編譯器和處理器對(duì)內(nèi)存模型的實(shí)現(xiàn)
編譯器和處理器希望內(nèi)存模型對(duì)它們的束縛越少越好,這樣它們就可以做盡可能多的優(yōu)化來(lái)提高性能感猛。編譯器和處理器希望實(shí)現(xiàn)一個(gè)弱內(nèi)存模型七扰。
另外還要一個(gè)特別有意思的事情就是關(guān)于重排序問(wèn)題,更簡(jiǎn)單的說(shuō)陪白,重排序可以分為兩類(lèi):
會(huì)改變程序執(zhí)行結(jié)果的重排序颈走。
不會(huì)改變程序執(zhí)行結(jié)果的重排序。
JMM對(duì)這兩種不同性質(zhì)的重排序咱士,采取了不同的策略立由,如下轧钓。
對(duì)于會(huì)改變程序執(zhí)行結(jié)果的重排序,JMM要求編譯器和處理器必須禁止這種重排序锐膜。
對(duì)于不會(huì)改變程序執(zhí)行結(jié)果的重排序毕箍,JMM對(duì)編譯器和處理器不做要求(JMM允許這種重排序)
JMM的設(shè)計(jì)圖為:
從圖可以看出:
JMM向程序員提供的happens-before規(guī)則能滿足程序員的需求。JMM的happens-before規(guī)則不但簡(jiǎn)單易懂枣耀,而且也向程序員提供了足夠強(qiáng)的內(nèi)存可見(jiàn)性保證(有些內(nèi)存可見(jiàn)性保證其實(shí)并不一定真實(shí)存在霉晕,比如上面的A happens-before B)。
JMM對(duì)編譯器和處理器的束縛已經(jīng)盡可能少捞奕。從上面的分析可以看出牺堰,JMM其實(shí)是在遵循一個(gè)基本原則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎么優(yōu)化都行颅围。
例如伟葫,如果編譯器經(jīng)過(guò)細(xì)致的分析后,認(rèn)定一個(gè)鎖只會(huì)被單個(gè)線程訪問(wèn)院促,那么這個(gè)鎖可以被消除筏养。
再如,如果編譯器經(jīng)過(guò)細(xì)致的分析后常拓,認(rèn)定一個(gè)volatile變量只會(huì)被單個(gè)線程訪問(wèn)渐溶,那么編譯器可以把這個(gè)volatile變量當(dāng)作一個(gè)普通變量來(lái)對(duì)待。這些優(yōu)化既不會(huì)改變程序的執(zhí)行結(jié)果弄抬,又能提高程序的執(zhí)行效率茎辐。
5.2 happens-before與JMM的關(guān)系
一個(gè)happens-before規(guī)則對(duì)應(yīng)于一個(gè)或多個(gè)編譯器和處理器重排序規(guī)則。對(duì)于Java程序員來(lái)說(shuō)掂恕,happens-before規(guī)則簡(jiǎn)單易懂拖陆,它避免Java程序員為了理解JMM提供的內(nèi)存可見(jiàn)性保證而去學(xué)習(xí)復(fù)雜的重排序規(guī)則以及這些規(guī)則的具體實(shí)現(xiàn)方法
5.3 今后可能需要關(guān)注的問(wèn)題
從上面內(nèi)存抽象結(jié)構(gòu)來(lái)說(shuō),可能出在數(shù)據(jù)“臟讀”的現(xiàn)象懊亡,這就是數(shù)據(jù)可見(jiàn)性的問(wèn)題依啰,另外,重排序在多線程中不注意的話也容易存在一些問(wèn)題店枣,比如一個(gè)很經(jīng)典的問(wèn)題就是DCL(雙重檢驗(yàn)鎖)速警,這就是需要禁止重排序,另外鸯两,在多線程下原子操作例如i++不加以注意的也容易出現(xiàn)線程安全的問(wèn)題坏瞄。
但總的來(lái)說(shuō),在多線程開(kāi)發(fā)時(shí)需要從原子性甩卓,有序性鸠匀,可見(jiàn)性三個(gè)方面進(jìn)行考慮。J.U.C包下的并發(fā)工具類(lèi)和并發(fā)容器也是需要花時(shí)間去掌握的逾柿,這些東西在以后得文章中多會(huì)一一進(jìn)行討論缀棍。
近期干貨文章推薦