受保護(hù)資源和鎖之間合理的關(guān)聯(lián)關(guān)系應(yīng)該是 N:1 的關(guān)系唆涝,也就是說(shuō)可以用一把鎖來(lái)保護(hù)多個(gè)資源脱羡,但是不能用多把鎖來(lái)保護(hù)一個(gè)資源.
當(dāng)我們要保護(hù)多個(gè)資源時(shí)萝究,首先要區(qū)分這些資源是否存在關(guān)聯(lián)關(guān)系
保護(hù)沒(méi)有關(guān)聯(lián)關(guān)系的多個(gè)資源
在現(xiàn)實(shí)世界里,球場(chǎng)的座位和電影院的座位就是沒(méi)有關(guān)聯(lián)關(guān)系的.這種場(chǎng)景非常容易理解,球賽有球賽的門票,電影院有電影院的門票,各自管理各自的.
同樣這對(duì)應(yīng)到編程領(lǐng)域,也很容易解決.例如.銀行業(yè)務(wù)中有針對(duì)賬戶余額(資源)的取款操作,也有針對(duì)賬戶密碼(資源)的更改操作,我們可以為賬戶余額和賬戶密碼分配不同的鎖來(lái)解決并發(fā)問(wèn)題,這個(gè)還是很簡(jiǎn)單的
賬戶類 Account 有兩個(gè)成員變量锉罐,分別是賬戶余額 balance 和賬戶密碼 password帆竹。取款 withdraw() 和查看余額 getBalance() 操作會(huì)訪問(wèn)賬戶余額balance,我們創(chuàng)建一個(gè) final 對(duì)象 balLock 作為鎖(類比球賽門票)脓规;而更改密碼updatePassword() 和查看密碼 getPassword() 操作會(huì)修改賬戶密碼 password栽连,我們創(chuàng)建一個(gè) final 對(duì)象 pwLock 作為鎖(類比電影票)。不同的資源用不同的鎖保護(hù)侨舆,各自管各自的秒紧,很簡(jiǎn)單
也可以用一把互斥鎖保護(hù)多個(gè)資源,可以用this這一把鎖管理賬戶類里所有的資源.在所有方法加上synchronized關(guān)鍵字就可以了.
但是,用一把鎖有個(gè)問(wèn)題,就是性能太差,會(huì)導(dǎo)致取款,查看余額,修改密碼,查看密碼這些操作是串行的.而用兩把鎖,取款和修改密碼是可以并行的.用不同的鎖對(duì)受保護(hù)資源進(jìn)行精細(xì)化管理绢陌,能夠提升性能。這種鎖還有個(gè)名字噩茄,叫細(xì)粒度鎖下面。
保護(hù)有關(guān)聯(lián)關(guān)系的多個(gè)資源
如果多個(gè)資源是有關(guān)聯(lián)關(guān)系的,那這個(gè)問(wèn)題就有點(diǎn)復(fù)雜了绩聘。例如銀行業(yè)務(wù)里面的轉(zhuǎn)賬操作沥割,賬戶 A 減少 100 元,賬戶 B 增加 100 元凿菩。這兩個(gè)賬戶就是有關(guān)聯(lián)關(guān)系的机杜。
聲明了個(gè)賬戶類:Account,該類有一個(gè)成員變量余額:balance衅谷,還有一個(gè)用于轉(zhuǎn)賬的方法:transfer()椒拗,然后怎么保證轉(zhuǎn)賬操作 transfer() 沒(méi)有并發(fā)問(wèn)題呢?
直接在transfer()方法前加上synchronized?
然而在this這把鎖上,this 這把鎖可以保護(hù)自己的余額 this.balance获黔,卻保護(hù)不了別人的余額 target.balance
具體分析一下蚀苛,假設(shè)有 A、B玷氏、C 三個(gè)賬戶堵未,余額都是 200 元,我們用兩個(gè)線程分別執(zhí)行兩個(gè)轉(zhuǎn)賬操作:賬戶 A 轉(zhuǎn)給賬戶 B 100 元盏触,賬戶 B 轉(zhuǎn)給賬戶 C 100 元渗蟹,最后我們期望的結(jié)果應(yīng)該是賬戶 A 的余額是 100 元,賬戶 B 的余額是 200 元赞辩, 賬戶 C 的余額是 300 元雌芽。
假設(shè)線程1執(zhí)行賬戶A轉(zhuǎn)賬B的操作,.線程2執(zhí)行賬戶B轉(zhuǎn)帳戶C的操作,這兩個(gè)線程分別在兩顆CPU上同時(shí)執(zhí)行,實(shí)際上它們不是互斥的.因?yàn)榫€程1鎖定的是賬戶A的實(shí)例(A.this),線程2鎖定的是賬戶B的實(shí)例(B.this),所以這兩個(gè)線程可以同時(shí)進(jìn)入臨界區(qū)transfer(),同時(shí)進(jìn)去意味者,線程1和線程2讀到的值都是200,導(dǎo)致最終賬戶B的余額可能是300或100.就是不可能是200
使用鎖的正確姿勢(shì)
同一把鎖來(lái)保護(hù)多個(gè)資源,只要我們的鎖能覆蓋所有受保護(hù)資源就可以了.
可以在賬戶類中定義一個(gè)變量,在創(chuàng)建賬戶類時(shí)要求傳入這個(gè)變量,這樣在創(chuàng)建Account對(duì)象時(shí)傳入相同的變量,就會(huì)共享這個(gè)鎖了.但是這要求傳入的變量必須時(shí)同一個(gè).
還有一種方案就是用Account.class作為共享的鎖.
總結(jié)
要分析多個(gè)資源之間的關(guān)系。如果資源之間沒(méi)有關(guān)系辨嗽,很好處理世落,每個(gè)資源一把鎖就可以了。如果資源之間有關(guān)聯(lián)關(guān)系召庞,就要選擇一個(gè)粒度更大的鎖岛心,這個(gè)鎖應(yīng)該能夠覆蓋所有相關(guān)的資源。
“原子性”的本質(zhì)是什么篮灼?其實(shí)不是不可分割忘古,不可分割只是外在表現(xiàn),其本質(zhì)是多個(gè)資源間有一致性的要求诅诱,操作的中間狀態(tài)對(duì)外不可見(jiàn)髓堪。例如,在 32 位的機(jī)器上寫 long 型變量有中間狀態(tài)(只寫了 64 位中的 32 位),在銀行轉(zhuǎn)賬的操作中也有中間狀態(tài)(賬戶 A減少了 100干旁,賬戶 B 還沒(méi)來(lái)得及發(fā)生變化)驶沼。所以解決原子性問(wèn)題,是要保證中間狀態(tài)對(duì)外不可見(jiàn)争群。