Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
902
2022-05-29
什么是并發(fā)三大特性
在并發(fā)編程中,去解決線程安全的問(wèn)題,一般可以從兩大核心和三大特性作為切入點(diǎn)來(lái)思考怎么去解決,兩大核心就是JMM內(nèi)存模型和happens-before規(guī)則,三大特性主要是原子性、可見(jiàn)性和有序性。并發(fā)關(guān)鍵字synchronized和volatile都涉及到了三大特性,說(shuō)明了三大特性的重要性。從這兩個(gè)關(guān)鍵字分析這三大特性。
一、原子性
什么是原子性:就是指一個(gè)操作是不可中斷的,要么執(zhí)行成功要么執(zhí)行失敗,在多線程中,一個(gè)線程正在執(zhí)行就不會(huì)被其他線程干擾。分析如下例子:
int a = 10; //1
a++; //2
int b=a; //3
a = a+1; //4
上面這四個(gè)語(yǔ)句中只有第1個(gè)語(yǔ)句是原子操作,將10賦值給線程工作內(nèi)存的變量a,而語(yǔ)句2(a++),實(shí)際上包含了三個(gè)操作:1. 讀取變量a的值;2:對(duì)a進(jìn)行加一的操作;3.將計(jì)算后的值再賦值給變量a,而這三個(gè)操作無(wú)法構(gòu)成原子操作。對(duì)語(yǔ)句3,4的分析同理可得這兩條語(yǔ)句不具備原子性。當(dāng)然,java內(nèi)存模型中定義了8中操作都是原子的,不可再分的。
lock(鎖定):作用于主內(nèi)存中的變量,它把一個(gè)變量標(biāo)識(shí)為一個(gè)線程獨(dú)占的狀態(tài);
unlock(解鎖):作用于主內(nèi)存中的變量,它把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后的變量才可以被其他線程鎖定
read(讀取):作用于主內(nèi)存的變量,它把一個(gè)變量的值從主內(nèi)存復(fù)制傳輸?shù)骄€程的工作內(nèi)存中,以便后面的load動(dòng)作使用;
load(載入):作用于工作內(nèi)存中的變量,它把read操作從主內(nèi)存中得到的變量值復(fù)制放入工作內(nèi)存中的變量副本
use(使用):作用于工作內(nèi)存中的變量,它把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用到變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作;
assign(賦值):作用于工作內(nèi)存中的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作;
store(存儲(chǔ)):作用于工作內(nèi)存的變量,它把工作內(nèi)存中一個(gè)變量的值同步傳送給主內(nèi)存中以便隨后的write操作使用;
write(操作):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中得到的變量的值同步放入主內(nèi)存的變量中。
上面的這些指令操作是相當(dāng)?shù)讓拥模梢宰鳛閿U(kuò)展知識(shí)面掌握下。那么如何理解這些指令了?比如,把一個(gè)變量從主內(nèi)存中復(fù)制到工作內(nèi)存中就需要執(zhí)行read,load操作,將工作內(nèi)存同步到主內(nèi)存中就需要執(zhí)行store,write操作。注意的是:java內(nèi)存模型只是要求上述兩個(gè)操作是順序執(zhí)行的并不是連續(xù)執(zhí)行的。也就是說(shuō)read和load之間可以插入其他指令,store和writer可以插入其他指令。比如對(duì)主內(nèi)存中的a,b進(jìn)行訪問(wèn)就可以出現(xiàn)這樣的操作順序:read a,read b, load b,load a。
由原子性變量操作read,load,use,assign,store,write,可以大致認(rèn)為基本數(shù)據(jù)類型的訪問(wèn)讀寫具備原子性(例外就是long和double的非原子性協(xié)定)
synchronized
上面一共有八條原子操作,其中六條可以滿足基本數(shù)據(jù)類型的訪問(wèn)讀寫具備原子性,還剩下lock和unlock兩條原子操作。如果我們需要更大范圍的原子性操作就可以使用lock和unlock原子操作。盡管jvm沒(méi)有把lock和unlock開(kāi)放給我們使用,但jvm以更高層次的指令monitorenter和monitorexit指令開(kāi)放給我們使用,反應(yīng)到j(luò)ava代碼中就是synchronized關(guān)鍵字,也就是說(shuō)synchronized滿足原子性。
volatile
我們先來(lái)看這樣一個(gè)例子:
public class VolatileExample { private static volatile int counter = 0; public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10000; i++) counter++; } }); thread.start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(counter); } }
開(kāi)啟10個(gè)線程,每個(gè)線程都自加10000次,如果不出現(xiàn)線程安全的問(wèn)題最終的結(jié)果應(yīng)該就是:10*10000 = 100000;可是運(yùn)行多次都是小于100000的結(jié)果,問(wèn)題在于 volatile并不能保證原子性,在前面說(shuō)過(guò)counter++這并不是一個(gè)原子操作,包含了三個(gè)步驟:1.讀取變量counter的值;2.對(duì)counter加一;3.將新值賦值給變量counter。如果線程A讀取counter到工作內(nèi)存后,其他線程對(duì)這個(gè)值已經(jīng)做了自增操作后,那么線程A的這個(gè)值自然而然就是一個(gè)過(guò)期的值,因此,總結(jié)果必然會(huì)是小于100000的。
如果讓volatile保證原子性,必須符合以下兩條規(guī)則:
運(yùn)算結(jié)果并不依賴于變量的當(dāng)前值,或者能夠確保只有一個(gè)線程修改變量的值;
變量不需要與其他的狀態(tài)變量共同參與不變約束
二、可見(jiàn)性
可見(jiàn)性是指當(dāng)一個(gè)線程修改了共享變量后,其他線程能夠立即得知這個(gè)修改。通過(guò)之前對(duì)synchronzed內(nèi)存語(yǔ)義進(jìn)行了分析,當(dāng)線程獲取鎖時(shí)會(huì)從主內(nèi)存中獲取共享變量的最新值,釋放鎖的時(shí)候會(huì)將共享變量同步到主內(nèi)存中。從而,synchronized具有可見(jiàn)性。同樣的在volatile分析中,會(huì)通過(guò)在指令中添加內(nèi)存屏障指令,以實(shí)現(xiàn)內(nèi)存可見(jiàn)性。因此, volatile具有可見(jiàn)性
三、有序性
synchronized
synchronized語(yǔ)義表示鎖在同一時(shí)刻只能由一個(gè)線程進(jìn)行獲取,當(dāng)鎖被占用后,其他線程只能等待。因此,synchronized語(yǔ)義就要求線程在訪問(wèn)讀寫共享變量時(shí)只能“串行”執(zhí)行,因此synchronized具有有序性。
volatile
在java內(nèi)存模型中說(shuō)過(guò),為了性能優(yōu)化,編譯器和處理器會(huì)進(jìn)行指令重排序;也就是說(shuō)java程序天然的有序性可以總結(jié)為:如果在本線程內(nèi)觀察,所有的操作都是有序的;如果在一個(gè)線程觀察另一個(gè)線程,所有的操作都是無(wú)序的。volatile包含禁止指令重排序的語(yǔ)義,其具有有序性。
四、總結(jié)
synchronized: 具有原子性,有序性和可見(jiàn)性; volatile:具有有序性和可見(jiàn)性
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。