《Java并發(fā)編程》如何用一把鎖保護(hù)多個(gè)資源(互斥鎖伏嗜,下)

上篇文章中提到受保護(hù)的資源和鎖之間合理的關(guān)系應(yīng)該是 N:1 的關(guān)系,也就是說用一把鎖來保護(hù)多個(gè)資源伐厌。
分兩種情況:

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

對比現(xiàn)實(shí)生活承绸,球場的座位和電影院的座位沒有關(guān)聯(lián)關(guān)系,這種場景非常容易解決挣轨,就是球場有球場的門票军熏,電影院有電影院的門票,各管各的卷扮。
對應(yīng)到編程領(lǐng)域荡澎,兩個(gè)沒有關(guān)聯(lián)關(guān)系的對象用不同的鎖來解決并發(fā)問題。
下面一段代碼中晤锹,賬戶密碼和余額沒有關(guān)聯(lián)關(guān)系摩幔。

class Account {
  // 鎖:保護(hù)賬戶余額
  private final Object balLock
    = new Object();
  // 賬戶余額  
  private Integer balance;
  // 鎖:保護(hù)賬戶密碼
  private final Object pwLock
    = new Object();
  // 賬戶密碼
  private String password;

  // 取款
  void withdraw(Integer amt) {
    synchronized(balLock) {
      if (this.balance > amt){
        this.balance -= amt;
      }
    }
  } 
  // 查看余額
  Integer getBalance() {
    synchronized(balLock) {
      return balance;
    }
  }

  // 更改密碼
  void updatePassword(String pw){
    synchronized(pwLock) {
      this.password = pw;
    }
  } 
  // 查看密碼
  String getPassword() {
    synchronized(pwLock) {
      return password;
    }
  }
}

當(dāng)然我們也可以用一把鎖來保護(hù),實(shí)現(xiàn)方法是所有的方法用synchronized修飾鞭铆,鎖為this或衡。但是性能太差。用不同的鎖來對資源進(jìn)行精細(xì)化管理车遂,能提升性能封断。這種鎖,叫細(xì)粒度鎖艰额。

2.保護(hù)有關(guān)聯(lián)關(guān)系的多個(gè)資源

例如兩個(gè)賬戶A和B澄港,A轉(zhuǎn)給B100元,A余額減少100柄沮,B余額增加一百回梧。這倆賬戶是有關(guān)聯(lián)關(guān)系的。以下為賬戶類代碼:

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

那么如何解決并發(fā)祖搓,是下面這樣嗎狱意?

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

看似沒有問題,但如果你真的覺得沒有問題拯欧,說明還沒有理解鎖和資源的對應(yīng)關(guān)系详囤。synchronized關(guān)鍵字在這里鎖的是this對象,卻無法鎖住target賬戶對象。this這把鎖能保護(hù)自己的余額balance藏姐,卻保護(hù)不了別人的余額隆箩。看圖:


用鎖 this 保護(hù) this.balance 和 target.balance的示意圖

假設(shè)還有一個(gè)賬戶C羔杨,初始每個(gè)賬戶均有200捌臊,現(xiàn)在線程1執(zhí)行A轉(zhuǎn)賬給B,線程2執(zhí)行B轉(zhuǎn)賬給C兜材,這倆線程分別同時(shí)在兩個(gè)CPU上執(zhí)行理澎,他們是互斥的嗎?我們期望是曙寡,但實(shí)際上不是糠爬。兩個(gè)線程分別同時(shí)鎖定A的實(shí)例和B的實(shí)例,同時(shí)進(jìn)入臨界區(qū)举庶,同時(shí)讀到B的余額為200执隧,所以最終結(jié)果是B可能是為100,也可能為300灯变,就是不可能為200殴玛。


并發(fā)轉(zhuǎn)賬示意圖

使用鎖的正確姿勢

那么如何用一把鎖保護(hù)多個(gè)資源呢,對比現(xiàn)實(shí)世界的“包場”添祸,只要我們的鎖能覆蓋所有受保護(hù)的資源就可以了。這里介紹兩種方式:
1.不同對象共享同一把鎖寻仗,所有對象持有一個(gè)唯一的對象就可以了刃泌。

class Account {
  private Object lock;
  private int balance;
//構(gòu)造函數(shù)私有
  private Account(){}
  // 創(chuàng)建 Account 時(shí)傳入同一個(gè) lock 對象
  public Account(Object lock) {
    this.lock = lock;
  } 
  // 轉(zhuǎn)賬
  void transfer(Account target, int amt){
    // 此處檢查所有對象共享的鎖
    synchronized(lock) {
      if (this.balance > amt) {
        this.balance -= amt;
        target.balance += amt;
      }
    }
  }
}

這的確能解決問題署尤,但是有個(gè)瑕疵耙替,他要求創(chuàng)建Account對象時(shí)候必須傳入同一個(gè)對象,如果傳入的不是同一個(gè)lock曹体,那就gg了俗扇。而且現(xiàn)實(shí)項(xiàng)目中,有時(shí)傳入共享的lock真的很難箕别,因此它缺乏的實(shí)踐的可行性铜幽。
2.使用XXX.class
這里Account.class是所有Account對象共享的,根據(jù)雙親委派原則可以保證它的唯一性串稀。

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;
      }
    }
  } 
}

這個(gè)算是更好的方法除抛,但是有個(gè)現(xiàn)實(shí)的問題你有注意到嗎?
所有的轉(zhuǎn)賬操作都變成串行的了母截。還能同時(shí)轉(zhuǎn)賬嗎到忽?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市清寇,隨后出現(xiàn)的幾起案子喘漏,更是在濱河造成了極大的恐慌护蝶,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翩迈,死亡現(xiàn)場離奇詭異滓走,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帽馋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門搅方,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绽族,你說我怎么就攤上這事姨涡。” “怎么了吧慢?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵涛漂,是天一觀的道長。 經(jīng)常有香客問我检诗,道長匈仗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任逢慌,我火速辦了婚禮悠轩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘攻泼。我一直安慰自己火架,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布忙菠。 她就那樣靜靜地躺著何鸡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牛欢。 梳的紋絲不亂的頭發(fā)上骡男,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機(jī)與錄音傍睹,去河邊找鬼隔盛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛焰望,可吹牛的內(nèi)容都是我干的骚亿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼熊赖,長吁一口氣:“原來是場噩夢啊……” “哼来屠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤俱笛,失蹤者是張志新(化名)和其女友劉穎捆姜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迎膜,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泥技,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磕仅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珊豹。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖榕订,靈堂內(nèi)的尸體忽然破棺而出店茶,到底是詐尸還是另有隱情,我是刑警寧澤劫恒,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布贩幻,位于F島的核電站,受9級特大地震影響两嘴,放射性物質(zhì)發(fā)生泄漏丛楚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一憔辫、第九天 我趴在偏房一處隱蔽的房頂上張望趣些。 院中可真熱鬧,春花似錦螺垢、人聲如沸喧务。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至庐冯,卻和暖如春孽亲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背展父。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工返劲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栖茉。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓篮绿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吕漂。 傳聞我的和親對象是個(gè)殘疾皇子亲配,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在上一篇中吼虎,我們提到受保護(hù)資源和鎖之間合理的關(guān)聯(lián)關(guān)系應(yīng)該是N:1 的關(guān)系犬钢, 也就是說可以用一把鎖來保護(hù)多個(gè)資源,但...
    逗逼程序員閱讀 308評論 1 0
  • 受保護(hù)資源和鎖之間合理的關(guān)聯(lián)關(guān)系應(yīng)該是 N:1 的關(guān)系,也就是說可以用一把鎖來保護(hù)多個(gè)資源洒疚,但是不能用多把鎖來保護(hù)...
    pixelczx閱讀 306評論 0 1
  • 本文出自 Eddy Wiki 歹颓,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,130評論 0 14
  • 一個(gè)或者多個(gè)操作在CPU執(zhí)行的過程中不被中斷的特性,稱為“原子性”油湖。注意巍扛,原子性是面向cpu指令級別操作的,而不是...
    頹靡浪蕩君閱讀 4,015評論 1 4
  • 書籍:職業(yè)指導(dǎo) 標(biāo)題:職業(yè)價(jià)值觀 價(jià)值觀是一種深植于內(nèi)心的信念肺魁,他們會(huì)在你面臨選擇時(shí)影響你的思考电湘。它為你在評估信息...
    梁琪_ad60閱讀 2,307評論 0 0