并发模型之——共享内存模型(线程与锁)理论篇
? ? ? 這里我們使用Java的線程與鎖來解析共享內(nèi)存模型;做過java開發(fā)并且了解線程安全問題的知道,要使某段代碼是線程安全的那必須要滿足兩個(gè)條件:內(nèi)存可見性、原子性;
內(nèi)存可見性
? ? ? 在JVM規(guī)定多個(gè)線程進(jìn)行通訊是通過共享變量進(jìn)行的,而Java內(nèi)存模型規(guī)定了有主內(nèi)存是所有線程共享的,而各個(gè)線程又有自己的工作內(nèi)存,線程只能訪問自己的工作內(nèi)存中數(shù)據(jù);
? ? ? 如:有一個(gè)共享變量x,兩個(gè)線程a、b變量x存儲(chǔ)在主內(nèi)存中然后又兩個(gè)x的拷貝分別存儲(chǔ)在a、b線程的工作內(nèi)存中線程a、b只能對自己工作內(nèi)存中的x的拷貝進(jìn)行操作,不可直接操作主內(nèi)存;
? ? ? 線程a對x修改時(shí)先把值放到自己的工作內(nèi)存中,然后再把工作內(nèi)存中的x拷貝更新到主內(nèi)存中,線程b同樣如此;當(dāng)線程a更新了主內(nèi)存后線程b刷新工作內(nèi)存后就能看到a更新后的最新值這就是內(nèi)存可見性問題;
? ? ? 內(nèi)存可見性要保證兩點(diǎn):1、線程修改后的共享變量更新到主內(nèi)存;2、從主內(nèi)存中更新最新值到工作內(nèi)存中;
? ? ? 內(nèi)存可見性:線程對共享變量的修改其他線程可以看到修改后的值;
? ?
原子性
? ? ? 當(dāng)線程引用共享變量時(shí),工作內(nèi)存中沒有共享變量時(shí)它會(huì)從主內(nèi)存復(fù)制共享變量到自己工作內(nèi)存中,當(dāng)工作內(nèi)存有共享變量時(shí)線程可能會(huì)從主內(nèi)存更新也有可能直接使用工作內(nèi)存中的共享變量;
有代碼塊,count為共享變量:
// count初始值為0,這時(shí)有a、b線程都執(zhí)行這行代碼,可能有不少人以為線程a , b執(zhí)行完成后count的值為2,但真實(shí)情況是count最終值可能為1也可能為2,因?yàn)檫@里有一個(gè)原子性問題;
? ? ?熟悉Java的都知道在Java中++count非原子操作,流程為:
? ? ? ? ? ?1、把主內(nèi)存共享變量count拷貝到工作內(nèi)存
? ? ? ? ? ?2、把工作內(nèi)存中count值+1
? ? ? ? ? ?3、把結(jié)果寫回更新回主內(nèi)存
? ? ?當(dāng)只有一個(gè)線程時(shí)這個(gè)操作沒有問題;
? ? ?當(dāng)有多個(gè)線程時(shí)有可能出現(xiàn):
? ? ? ? ? ?1、 線程a把主內(nèi)存共享變量count拷貝到工作內(nèi)存
? ? ? ? ? ?2、 線程b把主內(nèi)存共享變量count拷貝到工作內(nèi)存
? ? ? ? ? ?3、 線程a把工作內(nèi)存中count進(jìn)行+1
? ? ? ? ? ?4、 線程b把工作內(nèi)存中count進(jìn)行+1
? ? ? ? ? ?5、 線程a把工作內(nèi)存更新到主內(nèi)存
? ? ? ? ? ?6、 線程b把工作內(nèi)存更新到主內(nèi)存
? ? ? a,b線程執(zhí)行完后最終count的值只是1而不是我們期望得到的2,因?yàn)檫@里出現(xiàn)了多個(gè)線程交叉執(zhí)行導(dǎo)致破壞了程序的有序性,而且count+1操作又不是原子的,所以我們必須要保證這程序的原子性,可以使用Java中的synchronized(同步)或Lock機(jī)制來解決;
? ? ? 使用共享內(nèi)存模型進(jìn)行并發(fā)編程時(shí)必須要解決我們上面介紹的兩個(gè)點(diǎn):內(nèi)存可見性、原子性,但現(xiàn)在大部分編程語言原生都支持共享內(nèi)存模型方式的并發(fā)所以我們很容易就可以達(dá)到這兩個(gè)要求;
? ? ? 現(xiàn)在代碼的執(zhí)行要經(jīng)過多層的優(yōu)化對指令重排序,如:編譯器、處理器等級別的優(yōu)化,經(jīng)過這些優(yōu)化重排序后最終代碼執(zhí)行順序可能與之前是不一致的,在單線程時(shí)中編譯器、處理會(huì)保持as-if-serial,對不存在數(shù)據(jù)依賴的進(jìn)行重排序,所以不會(huì)出現(xiàn)重排序問題;但在多線程情況下就會(huì)出現(xiàn)問題,不過還好Java中有些機(jī)制可以使程序在編譯器、處理器優(yōu)化時(shí)會(huì)對有數(shù)據(jù)依賴的禁止指令重排序,如:volatile、synchronized等,所以我們可以很輕松應(yīng)對這問題;
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 指令重排序問題
? ? ? 在Java中我們要使代碼在多線程中同時(shí)滿足內(nèi)存可見性與有序性那就要使用Java提供給我們的同步與鎖機(jī)制如:synchronized、volatile、Lock、concurrent類等;
? ? ? 優(yōu)點(diǎn):共享內(nèi)存模型(線程與鎖)可以說是最常見的并發(fā)模型大多數(shù)編程語言都原生支持,也適合解決很多問題,通過線程與鎖實(shí)現(xiàn)起來相對也簡單點(diǎn);
? ? ? 缺點(diǎn):通過多線程實(shí)現(xiàn)并發(fā),而線程耗費(fèi)的資源比較多,線程總數(shù)有限制;通過共享內(nèi)存來實(shí)現(xiàn)多線程通訊又會(huì)涉及到鎖、竟態(tài)、死鎖等問題影響程序性能;一不小心就會(huì)陷入可見性問題、重排序問題等而且多線程程序不容易測試、維護(hù)等;
?
?文章首發(fā)地址:Solinx
http://www.solinx.co/archives/179
轉(zhuǎn)載于:https://www.cnblogs.com/softlin/p/4474838.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的并发模型之——共享内存模型(线程与锁)理论篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux服务器运行环境搭建(二)——R
- 下一篇: 用户代码未处理EntityCommand