Java并發(fā)之嵌套管程鎖死(Nested Monitor Lockout)

  • 嵌套管程死鎖是如何發(fā)生的
  • 具體的嵌套管程死鎖的例子
  • 嵌套管程死鎖 vs 死鎖

嵌套管程鎖死類似于死鎖森枪, 下面是一個嵌套管程鎖死的場景:

Thread 1 synchronizes on A
Thread 1 synchronizes on B (while synchronized on A)
Thread 1 decides to wait for a signal from another thread before continuing
Thread 1 calls B.wait() thereby releasing the lock on B, but not A.

Thread 2 needs to lock both A and B (in that sequence)
        to send Thread 1 the signal.
Thread 2 cannot lock A, since Thread 1 still holds the lock on A.
Thread 2 remain blocked indefinately waiting for Thread1
        to release the lock on A

Thread 1 remain blocked indefinately waiting for the signal from
        Thread 2, thereby
        never releasing the lock on A, that must be released to make
        it possible for Thread 2 to send the signal to Thread 1, etc.

線程1獲得A對象的鎖牺六。
線程1獲得對象B的鎖(同時持有對象A的鎖)。
線程1決定等待另一個線程的信號再繼續(xù)褒傅。
線程1調(diào)用B.wait()缎岗,從而釋放了B對象上的鎖遏佣,但仍然持有對象A的鎖。

線程2需要同時持有對象A和對象B的鎖讯壶,才能向線程1發(fā)信號料仗。
線程2無法獲得對象A上的鎖,因為對象A上的鎖當(dāng)前正被線程1持有伏蚊。
線程2一直被阻塞立轧,等待線程1釋放對象A上的鎖。

線程1一直阻塞,等待線程2的信號氛改,因此帐萎,不會釋放對象A上的鎖,
    而線程2需要對象A上的鎖才能給線程1發(fā)信號……

我們看下面這個實際的例子:

//lock implementation with nested monitor lockout problem

public class Lock{
  protected MonitorObject monitorObject = new MonitorObject();
  protected boolean isLocked = false;

  public void lock() throws InterruptedException{
    synchronized(this){
      while(isLocked){
        synchronized(this.monitorObject){
            this.monitorObject.wait();
        }
      }
      isLocked = true;
    }
  }

  public void unlock(){
    synchronized(this){
      this.isLocked = false;
      synchronized(this.monitorObject){
        this.monitorObject.notify();
      }
    }
  }
}

可以看到胜卤,lock()方法首先在”this”上同步疆导,然后在monitorObject上同步。如果isLocked等于false葛躏,因為線程不會繼續(xù)調(diào)用monitorObject.wait()澈段,那么一切都沒有問題 。但是如果isLocked等于true舰攒,調(diào)用lock()方法的線程會在monitorObject.wait()上阻塞败富。

這里的問題在于,調(diào)用monitorObject.wait()方法只釋放了monitorObject上的管程對象芒率,而與”this“關(guān)聯(lián)的管程對象并沒有釋放囤耳。換句話說,這個剛被阻塞的線程仍然持有”this”上的鎖偶芍。

(校對注:如果一個線程持有這種Lock的時候另一個線程執(zhí)行了lock操作)當(dāng)一個已經(jīng)持有這種Lock的線程想調(diào)用unlock(),就會在unlock()方法進入synchronized(this)塊時阻塞充择。這會一直阻塞到在lock()方法中等待的線程離開synchronized(this)塊。但是匪蟀,在unlock中isLocked變?yōu)閒alse椎麦,monitorObject.notify()被執(zhí)行之后,lock()中等待的線程才會離開synchronized(this)塊材彪。

簡而言之观挎,在lock方法中等待的線程需要其它線程成功調(diào)用unlock方法來退出lock方法,但是段化,在lock()方法離開外層同步塊之前嘁捷,沒有線程能成功執(zhí)行unlock()。

結(jié)果就是显熏,任何調(diào)用lock方法或unlock方法的線程都會一直阻塞雄嚣。這就是嵌套管程鎖死。

具體的嵌套管程死鎖的例子

例如喘蟆,如果你準(zhǔn)備實現(xiàn)一個公平鎖缓升。你可能希望每個線程在它們各自的QueueObject上調(diào)用wait(),這樣就可以每次喚醒一個線程蕴轨。

//Fair Lock implementation with nested monitor lockout problem

public class FairLock {
  private boolean           isLocked       = false;
  private Thread            lockingThread  = null;
  private List<QueueObject> waitingThreads =
            new ArrayList<QueueObject>();

  public void lock() throws InterruptedException{
    QueueObject queueObject = new QueueObject();

    synchronized(this){
      waitingThreads.add(queueObject);

      while(isLocked || waitingThreads.get(0) != queueObject){

        synchronized(queueObject){
          try{
            queueObject.wait();
          }catch(InterruptedException e){
            waitingThreads.remove(queueObject);
            throw e;
          }
        }
      }
      waitingThreads.remove(queueObject);
      isLocked = true;
      lockingThread = Thread.currentThread();
    }
  }

  public synchronized void unlock(){
    if(this.lockingThread != Thread.currentThread()){
      throw new IllegalMonitorStateException(
        "Calling thread has not locked this lock");
    }
    isLocked      = false;
    lockingThread = null;
    if(waitingThreads.size() > 0){
      QueueObject queueObject = waitingThread.get(0);
      synchronized(queueObject){
        queueObject.notify();
      }
    }
  }
}

乍看之下港谊,嗯,很好橙弱,但是請注意lock方法是怎么調(diào)用queueObject.wait()的歧寺,在方法內(nèi)部有兩個synchronized塊燥狰,一個鎖定this,一個嵌在上一個synchronized塊內(nèi)部成福,它鎖定的是局部變量queueObject碾局。
當(dāng)一個線程調(diào)用queueObject.wait()方法的時候,它僅僅釋放的是在queueObject對象實例的鎖奴艾,并沒有釋放”this”上面的鎖净当。

現(xiàn)在我們還有一個地方需要特別注意, unlock方法被聲明成了synchronized蕴潦,這就相當(dāng)于一個synchronized(this)塊像啼。這就意味著,如果一個線程在lock()中等待潭苞,該線程將持有與this關(guān)聯(lián)的管程對象忽冻。所有調(diào)用unlock()的線程將會一直保持阻塞,等待著前面那個已經(jīng)獲得this鎖的線程釋放this鎖此疹,但這永遠也發(fā)生不了僧诚,因為只有某個線程成功地給lock()中等待的線程發(fā)送了信號,this上的鎖才會釋放蝗碎,但只有執(zhí)行unlock()方法才會發(fā)送這個信號湖笨。

因此,上面的公平鎖的實現(xiàn)會導(dǎo)致嵌套管程鎖死蹦骑。

Nested Monitor Lockout vs. Deadlock

嵌套管程鎖死與死鎖很像:都是線程最后被一直阻塞著互相等待慈省。
但是兩者又不完全相同。在死鎖中我們已經(jīng)對死鎖有了個大概的解釋眠菇,死鎖通常是因為兩個線程獲取鎖的順序不一致造成的边败,線程1鎖住A,等待獲取B捎废,線程2已經(jīng)獲取了B笑窜,再等待獲取A。如死鎖避免中所說的登疗,死鎖可以通過總是以相同的順序獲取鎖來避免排截。但是發(fā)生嵌套管程鎖死時鎖獲取的順序是一致的。線程1獲得A和B谜叹,然后釋放B匾寝,等待線程2的信號搬葬。線程2需要同時獲得A和B荷腊,才能向線程1發(fā)送信號。所以急凰,一個線程在等待喚醒女仰,另一個線程在等待想要的鎖被釋放猜年。
不同點歸納如下:

In deadlock, two threads are waiting for each other to release locks.

In nested monitor lockout, Thread 1 is holding a lock A, and waits
for a signal from Thread 2. Thread 2 needs the lock A to send the
signal to Thread 1.

死鎖中,二個線程都在等待對方釋放鎖疾忍。

嵌套管程鎖死中乔外,線程1持有鎖A,同時等待從線程2發(fā)來的信號一罩,線程2需要鎖A來發(fā)信號給線程1杨幼。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市聂渊,隨后出現(xiàn)的幾起案子差购,更是在濱河造成了極大的恐慌,老刑警劉巖汉嗽,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欲逃,死亡現(xiàn)場離奇詭異,居然都是意外死亡饼暑,警方通過查閱死者的電腦和手機稳析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弓叛,“玉大人彰居,你說我怎么就攤上這事⌒奥耄” “怎么了裕菠?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闭专。 經(jīng)常有香客問我奴潘,道長,這世上最難降的妖魔是什么影钉? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任画髓,我火速辦了婚禮,結(jié)果婚禮上平委,老公的妹妹穿的比我還像新娘奈虾。我一直安慰自己,他們只是感情好廉赔,可當(dāng)我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布肉微。 她就那樣靜靜地躺著,像睡著了一般蜡塌。 火紅的嫁衣襯著肌膚如雪碉纳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天馏艾,我揣著相機與錄音劳曹,去河邊找鬼奴愉。 笑死,一個胖子當(dāng)著我的面吹牛铁孵,可吹牛的內(nèi)容都是我干的锭硼。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼蜕劝,長吁一口氣:“原來是場噩夢啊……” “哼檀头!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岖沛,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤鳖擒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后烫止,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蒋荚,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年馆蠕,在試婚紗的時候發(fā)現(xiàn)自己被綠了期升。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡互躬,死狀恐怖播赁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吼渡,我是刑警寧澤容为,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站寺酪,受9級特大地震影響坎背,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寄雀,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一得滤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盒犹,春花似錦懂更、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卓嫂,卻和暖如春慷暂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背命黔。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工呜呐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悍募。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓蘑辑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坠宴。 傳聞我的和親對象是個殘疾皇子洋魂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,937評論 2 361

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