出現(xiàn)線程安全問題顾犹,一般是因?yàn)橹鞔婧凸ぷ鲀?nèi)存數(shù)據(jù)不一致以及重排序,那今天就說一下這兩個(gè)方面。
亂序執(zhí)行優(yōu)化
亂序執(zhí)行優(yōu)化是多核CPU為了提高效率而做的不符合代碼規(guī)則的優(yōu)化度宦。
int a = 1;
int b = 1;
int c = a + b;
- 正常我們看到的執(zhí)行順序是A-B-C式撼,但是因?yàn)镃PU的重排序童社,運(yùn)行順序有可能變?yōu)锽-A-C,這時(shí)候結(jié)果是不會受到任何影響的著隆。
JMM
-
說到并發(fā)就要設(shè)計(jì)多個(gè)線程之間是如何通信的扰楼,通信可以分為兩種:消息傳遞以及內(nèi)存共享,而java主要使用到內(nèi)存共享美浦。說到內(nèi)存共享就要先來看一下java的內(nèi)存結(jié)構(gòu)了弦赖。
image.png - Java的內(nèi)存結(jié)構(gòu)主要分為棧和堆。
- 棧
- 棧的速度僅次于寄存器浦辨,速度快蹬竖,但是大小和生存周期是固定的
- 主要用來存放一些基本類型的局部變量。
- 不同的線程棧之間是相互獨(dú)立的,存放的變量對于其他線程不可見币厕,即使他們運(yùn)行的是同一段代碼列另。
- 堆
- 是運(yùn)行的數(shù)據(jù)區(qū),是動(dòng)態(tài)分配的(比如new關(guān)鍵字)旦装,所以速度存取速度較慢
- 存在垃圾回收页衙,所以大小和生命周期是不確定的。
- 是共享的同辣,主要用來存放對象
enter image description here
- 當(dāng)一個(gè)線程棧持有一個(gè)對象拷姿,他只是持有了這個(gè)對象的引用(內(nèi)存地址),真正的對象還是在堆上旱函。
- 一個(gè)對象的成員變量响巢,無論是啥類型,都是跟著對象存在于堆上的棒妨。
- 這時(shí)候就有個(gè)問題踪古,如果多個(gè)線程,同時(shí)訪問同一個(gè)對象的同一個(gè)方法券腔,每個(gè)方法內(nèi)又有局部變量伏穆。會發(fā)生啥呢。
- 其實(shí)啥也不會發(fā)生纷纫,多個(gè)線程訪問同一個(gè)方法枕扫,他們其實(shí)只是持有方法內(nèi)局部變量的拷貝。
硬件結(jié)構(gòu)和JMM的關(guān)系
enter image description here
- 如果要從內(nèi)存中讀取數(shù)據(jù)處理后寫回辱魁,都經(jīng)歷了什么(從硬件角度來看)
- 首先cpu發(fā)出指令
- cache會在合適的時(shí)機(jī)把數(shù)據(jù)從內(nèi)存讀過來
- cpu寄存器去執(zhí)行計(jì)算操作啥的
- 寄存器把數(shù)據(jù)寫回到cache
- cache在合適的時(shí)機(jī)把數(shù)據(jù)(上一個(gè)博客中提到的緩存行)寫回主存\
- 硬件結(jié)構(gòu)和JMM有啥關(guān)系呢
- 咱們上面說的棧和堆是在主存里的~當(dāng)然烟瞧,也會在不同的時(shí)機(jī)存在于cpu的緩存和寄存器(寄存器的速度比緩存還要快)
硬件結(jié)構(gòu)和JMM更抽象的關(guān)系
image.png
- 前面說啦,java中線程的通信是通過內(nèi)存的共享染簇,那如果要完成一次通信参滴,要經(jīng)過這兩步
- 線程1從主存中把變量讀到了本地內(nèi)存,進(jìn)行一系列操作后锻弓,寫回到主存
- 線程2把數(shù)據(jù)從主存中讀到本地內(nèi)存
- 這里說的本地內(nèi)存是一個(gè)抽象的概念砾赔,他包括了上面說的,cache/寄存器啥的青灼。
- 那線程安全為啥會產(chǎn)生呢暴心,就是因?yàn)檫@個(gè)操作(基本上線程安全問題都是性能優(yōu)化導(dǎo)致的)
- 如果一個(gè)線程要使用一個(gè)共享變量,首先要讀到本地內(nèi)存杂拨,后續(xù)的讀寫操作都是作用于本地內(nèi)存上的副本的酷勺,執(zhí)行完一系列操作后某個(gè)時(shí)刻,這個(gè)共享變量才會被從本地內(nèi)存寫回到主存扳躬。
- 注意,在寫回到主存前,對于數(shù)據(jù)所執(zhí)行的操作對于別的線程是不可見的贷币。
- 如果此時(shí)另一個(gè)線程讀取了主存中的共享變量击胜,就產(chǎn)生了“臟讀“∫畚疲可以使用關(guān)鍵字”volatile“來解決偶摔,這個(gè)后續(xù)會詳細(xì)說。
- 內(nèi)存可見性JMM是通過happens-before關(guān)系來提供的促脉。這個(gè)在參考資料有詳細(xì)說辰斋,后面用到的時(shí)候也會寫出來。
接下來干啥
- 明天說一下同步操作和同步規(guī)則后瘸味,理論的知識就暫時(shí)到這里宫仗。接下來會說一下并發(fā)模擬用到的工具以及代碼。
更詳細(xì)的參考資料:
http://ifeve.com/java-memory-model-6/
https://juejin.im/post/5ae6d309518825673123fd0e