任意線程對 Object(Object 由 synchronized 保護)的訪問,首先要得到 Object 的監視器。若是獲取失敗,線程進入同步隊列,線程狀態變爲 BLOCKED。當訪問 Object 的
前驅(得到了鎖的線程)釋放了鎖,則該釋放操做喚醒阻塞在同步隊列中的線程,使其從新嘗試對監視器的獲取。
回顧線程的競爭機制
再來回顧一下線程的競爭機制對於鎖升級這塊的一些基本流程。方便你們更好的理解加入有這樣一個同步代碼塊,存在 Thread#一、Thread#2 等多個線程
synchronized (lock) {
// do something
}
狀況一:只有 Thread#1 會進入臨界區;
狀況二:Thread#1 和 Thread#2 交替進入臨界區,競爭不激烈;
狀況三:Thread#1/Thread#2/Thread3… 同時進入臨界區,競爭激烈
偏向鎖
此時當 Thread#1 進入臨界區時,JVM 會將 lockObject 的對象頭 Mark Word 的鎖標誌位設爲「01」,同時會用 CAS 操做把 Thread#1 的線程 ID 記錄到 Mark Word 中,此時進
入偏向模式。所謂「偏向」,指的是這個鎖會偏向於 Thread#1,若接下來沒有其餘線程進入臨界區,則 Thread#1 再出入臨界區無需再執行任何同步操做。也就是說,若只有
Thread#1 會進入臨界區,實際上只有 Thread#1 初次進入臨界區時須要執行 CAS 操做,之後再出入臨界區都不會有同步操做帶來的開銷。
輕量級鎖
偏向鎖的場景太過於理想化,更多的時候是 Thread#2 也會嘗試進入臨界區, 若是 Thread#2 也進入臨界區可是Thread#1 尚未執行完同步代碼塊時,會暫停 Thread#1而且升
級到輕量級鎖。Thread#2 經過自旋再次嘗試以輕量級鎖的方式來獲取鎖
重量級鎖
若是 Thread#1 和 Thread#2 正常交替執行,那麼輕量級鎖基本可以知足鎖的需求。可是若是 Thread#1 和 Thread#2同時進入臨界區,那麼輕量級鎖就會膨脹爲重量級鎖,意
味着 Thread#1 線程得到了重量級鎖的狀況下,Thread#2就會被阻塞
Synchronized 結合 Java Object 對象中的wait,notify,notifyAll
前面咱們在講 synchronized 的時候,發現被阻塞的線程何時被喚醒,取決於得到鎖的線程何時執行完同步代碼塊而且釋放鎖。那怎麼作到顯示控制呢?咱們就須要
借 助 一 個 信 號 機 制 : 在 Object 對 象 中 , 提 供 了wait/notify/notifyall,能夠用於控制線程的狀態
wait/notify/notifyall 基本概念
wait:表示持有對象鎖的線程 A 準備釋放對象鎖權限,釋放 cpu 資源並進入等待狀態。
notify:表示持有對象鎖的線程 A 準備釋放對象鎖權限,通知 jvm 喚 醒 某 個 競 爭 該 對 象 鎖 的 線 程 X 。 線 程 Asynchronized 代碼執行結束而且釋放了鎖以後,線程 X 直
接得到對象鎖權限,其餘競爭線程繼續等待(即便線程 X 同步完畢,釋放對象鎖,其餘競爭線程仍然等待,直至有新的 notify ,notifyAll 被調用)。
notifyAll:notifyall 和 notify 的區別在於,notifyAll 會喚醒全部競爭同一個對象鎖的全部線程,當已經得到鎖的線程A 釋放鎖以後,全部被喚醒的線程都有可能得到對象鎖權限
須要注意的是:三個方法都必須在 synchronized 同步關鍵字 所 限 定 的 做 用 域 中 調 用 , 否 則 會 報 錯java.lang.IllegalMonitorStateException ,意思是由於沒有同步,因此
線程對對象鎖的狀態是不肯定的,不能調用這些方法。另外,經過同步機制來確保線程從 wait 方法返回時可以感知到感知到 notify 線程對變量作出的修改