溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

java中如何用一把鎖保護(hù)多個(gè)資源

發(fā)布時(shí)間:2021-11-15 16:27:17 來源:億速云 閱讀:207 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“java中如何用一把鎖保護(hù)多個(gè)資源”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“java中如何用一把鎖保護(hù)多個(gè)資源”吧!

保護(hù)多個(gè)資源

保護(hù)多個(gè)沒有關(guān)系的資源

如果多個(gè)資源沒有關(guān)系,那就是保護(hù)一個(gè)資源模型的復(fù)制,同樣非常簡單,且看下圖:

java中如何用一把鎖保護(hù)多個(gè)資源

比如現(xiàn)實(shí)中銀行取款和修改密碼操作。 銀行取款操作對應(yīng)的資源是「余額」, 修改密碼操作對應(yīng)的資源是「密碼」,余額和密碼兩個(gè)資源完全沒有關(guān)系,所以各自用自家的鎖保護(hù)自家的資源就好了

如果多個(gè)資源沒有關(guān)系,程序猿的世界該有多美好,可惜并不是,我們保護(hù)的資源多數(shù)情況都有關(guān)聯(lián)關(guān)系

保護(hù)多個(gè)關(guān)系的資源

拿經(jīng)典的銀行轉(zhuǎn)賬案例來說明,賬戶 A 給賬戶 B 轉(zhuǎn)賬,賬戶 A 余額減少 100 元,賬戶 B 余額增加 100 元,這個(gè)操作要是原子性的,那么資源「A 余額」和資源「B 余額」就這樣"有了關(guān)系",先來看程序:

class Account {
  private int balance;
  // 轉(zhuǎn)賬
  synchronized void transfer(
      Account target, int amt){
    if (this.balance > amt) {
      this.balance -= amt;
      target.balance += amt;
    }
  } 
}

用 synchronized 直接保護(hù) transfer 方法,然后操作資源「A 余額」和資源「B 余額」就可以了

??: 真的是這樣嗎?

先停止向下看,在你的筆記本上按照文章開頭的三步走來畫個(gè)圖看一看,是否和下圖一樣呢?

java中如何用一把鎖保護(hù)多個(gè)資源

我們通常容易忽略鎖和資源的指向關(guān)系,我們想當(dāng)然的用鎖 this 來保護(hù) target 資源了,也就沒有起到保護(hù)作用

假設(shè) A,B,C 賬戶初始余額都是 200 原,A 向 B 轉(zhuǎn)賬 100,B 向 C 轉(zhuǎn)賬 100

我們期盼最終的結(jié)果是: 賬戶 A 余額: 100 元 賬戶 B 余額: 200 元 賬戶 C 余額: 300 元

假線程 1「A 向 B 轉(zhuǎn)賬」與線程 2「B 向 C 轉(zhuǎn)賬」兩個(gè)操作同時(shí)執(zhí)行,根據(jù) JMM 模型可知,線程 1 和線程 2 讀取線程 B 當(dāng)前的余額都是 200 元:

  • 線程 1 執(zhí)行 transfer 方法鎖定的是 A 的實(shí)例(A.this),并沒有鎖定 B 的實(shí)例

  • 線程 2 執(zhí)行 transfer 方法鎖定的是 B 的實(shí)例(B.this),并沒有鎖定 C 的實(shí)例

所以線程 1 和線程 2 可以同時(shí)進(jìn)入 transfer 臨界區(qū),上面你認(rèn)為對的模型其實(shí)就會(huì)變成這個(gè)樣子:

java中如何用一把鎖保護(hù)多個(gè)資源

還記得 happens-before 規(guī)則 這篇文章提到的監(jiān)視器鎖規(guī)則傳遞性規(guī)則嗎?

####監(jiān)視器鎖規(guī)則 對一個(gè)鎖的解鎖 happens-before 于隨后對這個(gè)鎖的加鎖 ####傳遞性規(guī)則 如果 A happens-before B, 且 B happens-before C, 那么 A happens-before C

資源 B.balance 存在于兩個(gè)"臨界區(qū)"中,所以這個(gè)"臨界區(qū)"對 B.balance 來說形同虛設(shè),也就不滿足監(jiān)視器鎖規(guī)則,進(jìn)而導(dǎo)致傳遞性規(guī)則也不生效,說白了,前序線程的更改結(jié)果對后一個(gè)線程不可見

這樣最終導(dǎo)致:

  • **賬戶 B 的余額可能是 100: ** 線程 1 寫 B.balance 100(balance = 300) 先于 線程 2 寫 B.balance(balance = 100),也就是說線程 1 的結(jié)果會(huì)被線程 2 覆蓋,導(dǎo)致最終賬戶 B 的余額為 100

  • 賬戶 B 的余額可能是 300: 與上述情況相反,線程 1 寫 B.balance 100(balance = 300) 后于 線程 2 寫 B.balance(balance = 100),也就是說線程 2 的結(jié)果線程 1 覆蓋,導(dǎo)致最終賬戶 B 的余額為 300

就是不能得到我們理想結(jié)果 200,感覺生活無比的艱難,那怎么辦呢?

正確姿勢

上面的問題就是為資源創(chuàng)建的鎖不能保護(hù)所有關(guān)聯(lián)的資源,那我們就想辦法解決這個(gè)問題,來看下面代碼:

class Account {
  private int balance;
  // 轉(zhuǎn)賬
  void transfer(Account target, int amt){
    synchronized(Account.class) {
      if (this.balance > amt) {
        this.balance -= amt;
        target.balance += amt;
      }
    }
  } 
}

我們將 this 鎖變?yōu)?Account.class 鎖,Account.class 是虛擬機(jī)加載 Account 類時(shí)創(chuàng)建的,肯定是唯一的(雙親委派模型解釋了為何該對象是唯一的), 所有 Account 對象都共享 Account.class, 也就是說,Account.class 鎖能保護(hù)所有 Account 對象,我們將上面程序再用模型解釋一下

java中如何用一把鎖保護(hù)多個(gè)資源

感謝各位的閱讀,以上就是“java中如何用一把鎖保護(hù)多個(gè)資源”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對java中如何用一把鎖保護(hù)多個(gè)資源這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI