天天看點

Java同步鎖何時釋放?

在測試java多線程中有關 “生産者和消費者” 這個經典問題的時候,寫代碼測試的時候,思考到一些問題(是以還是要動手,實踐才能儲真知啊), synchronize 同步鎖何時釋放,何時獲得?重新獲得鎖資源之後,代碼時從哪裡開始繼續執行的呢?

了解到,鎖用到的對象上上面原來有兩種池, 1)對象的鎖池, 2)對象的等待池。

執行線程sleep: 不會釋放cpu資源,也不會釋放鎖資源。

執行wait:  釋放cpu資源,也會釋放鎖資源。

-----------------------------------------------------------------------

是以總結有:

在Java對象中,有兩種池 

瑣池-----------------------synchronized 

等待池---------------------wait(),notify(),notifyAll() 

如果一個線程調用了某個對象的wait方法,那麼該線程進入到該對象的等待池中(并且已經将鎖釋放), 

如果未來的某一時刻,另外一個線程調用了相同對象的notify方法或者notifyAll方法, 

那麼該等待池中的線程就會被喚起,然後進入到對象的鎖池裡面去獲得該對象的鎖, 

如果獲得鎖成功後,那麼該線程就會沿着wait方法之後的路徑繼續執行。注意是沿着wait方法之後

其他答案:

    由于等待一個鎖定線程隻有在獲得這把鎖之後,才能恢複運作,是以讓持有鎖的線程在不需要鎖的時候及時釋放鎖是很重要的。在以下情況下,持有鎖的線程會釋放鎖:

    1. 執行完同步代碼塊。

    2. 在執行同步代碼塊的過程中,遇到異常而導緻線程終止。

    3. 在執行同步代碼塊的過程中,執行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進行對象的等待池。

    除了以上情況外,隻要持有鎖的此案吃還沒有執行完同步代碼塊,就不會釋放鎖。是以在以下情況下,線程不會釋放鎖:

    1. 在執行同步代碼塊的過程中,執行了Thread.sleep()方法,目前線程放棄CPU,開始睡眠,在睡眠中不會釋放鎖。

    2. 在執行同步代碼塊的過程中,執行了Thread.yield()方法,目前線程放棄CPU,但不會釋放鎖。

    3. 在執行同步代碼塊的過程中,其他線程執行了目前對象的suspend()方法,目前線程被暫停,但不會釋放鎖。但Thread類的suspend()方法已經被廢棄。

    避免死鎖的一個通用的經驗法則是:當幾個線程都要通路共享資源A、B和C時,保證使每個線程都按照同樣的順序去通路他們,比如都先通路A,再通路B和C。

    java.lang.Object類中提供了兩個用于線程通信的方法:wait()和notify()。需要注意到是,wait()方法必須放在一個循環中,因為在多線程環境中,共享對象的狀态随時可能改變。當一個在對象等待池中的線程被喚醒後,并不一定立即恢複運作,等到這個線程獲得了鎖及CPU才能繼續運作,又可能此時對象的狀态已經發生了變化。

    # 調用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫在synchronized(obj) {...} 代碼段内。

  

  # 調用obj.wait()後,線程A就釋放了obj的鎖,否則線程B無法獲得obj鎖,也就無法在synchronized(obj) {...} 代碼段内喚醒A。

  # 當obj.wait()方法傳回後,線程A需要再次獲得obj鎖,才能繼續執行。

  # 如果A1,A2,A3都在obj.wait(),則B調用obj.notify()隻能喚醒A1,A2,A3中的一個(具體哪一個由JVM決定)。

  # obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續執行obj.wait()的下一條語句,必須獲得obj鎖,是以,A1,A2,A3隻有一個有機會獲得鎖繼續執行,例如A1,其餘的需要等待A1釋放obj鎖之後才能繼續執行。

  # 當B調用obj.notify/notifyAll的時候,B正持有obj鎖,是以,A1,A2,A3雖被喚醒,但是仍無法獲得obj鎖。直到B退出synchronized塊,釋放obj鎖後,A1,A2,A3中的一個才有機會獲得鎖繼續執行。

  wait()/sleep()的差別

  前面講了wait/notify機制,Thread還有一個sleep()靜态方法,它也能使線程暫停一段時間。sleep與wait的不同點是:sleep并不釋放鎖,并且sleep的暫停和wait暫停是不一樣的。obj.wait會使線程進入obj對象的等待集合中并等待喚醒。

  但是wait()和sleep()都可以通過interrupt()方法打斷線程的暫停狀态,進而使線程立刻抛出InterruptedException。

  如果線程A希望立即結束線程B,則可以對線程B對應的Thread執行個體調用interrupt方法。如果此刻線程B正在wait/sleep/join,則線程B會立刻抛出InterruptedException,在catch() {} 中直接return即可安全地結束線程。

  需要注意的是,InterruptedException是線程自己從内部抛出的,并不是interrupt()方法抛出的。對某一線程調用interrupt()時,如果該線程正在執行普通的代碼,那麼該線程根本就不會抛出InterruptedException。但是,一旦該線程進入到wait()/sleep()/join()後,就會立刻抛出InterruptedException。

繼續閱讀