從網上看了不少批量重偏向與批量撤銷的介紹,但都只知其一;不知其二,本着鑽研的精神,使用數據進行分析批量重偏向與批量撤銷的工做機制。java
首先,要先知道偏向鎖的偏向鎖機制,着重看下撤銷機制。
而後,要知道【批量重偏向與批量撤銷】的研究對象是Class,即鎖對象對應的Class,而非鎖對象自己。jvm
// 研究的是對象.class synchronized(對象) { } // 並非說的這種,這種不考慮 synchronized(對象.class) { }
如對機制不瞭解,能夠看下這個博客 http://www.javashuo.com/article/p-kqdfoqoq-gt.html
另外幾個jvm參數:spa
-XX:BiasedLockingBulkRebiasThreshold = 20 // 默認偏向鎖批量重偏向閾值 -XX:BiasedLockingBulkRevokeThreshold = 40 // 默認偏向鎖批量撤銷閾值 -XX:+UseBiasedLocking // 使用偏向鎖,jdk6以後默認開啓 -XX:BiasedLockingStartupDelay = 0 // 延遲偏向時間, 默認不爲0,意思爲jvm啓動多少ms之後開啓偏向鎖機制(此處設爲0,不延遲)
在這裏,我把博客中的圖拷過來,方便你們思考。.net
咱們主要看下圖中的 「是否開啓重偏向?」 這個條件分支,來看下什麼是重偏向?線程
重偏向字面理解就是從新偏向,那什麼狀況下會從新偏向呢
咱們首先要知道的是,鎖對象Class剛開始的狀況下,是沒有開啓重偏向的,意思就是 「是否開啓重偏向?」 這個分支剛開始的時候是一直走 「否」 的,即會一直撤銷偏向鎖 ,當達到BiasedLockingBulkRebiasThreshold(20)次數的時候,容許重偏向。code
下面咱們看下代碼對象
public class TestBiasedLocking { public static void main(String[] args) throws DecoderException, InterruptedException { // 首先咱們建立一個list,來存放鎖對象 List<TestBiasedLocking> list = new LinkedList<>(); // 線程1 new Thread(() -> { for (int i = 0; i < 50; i++) { TestBiasedLocking testBiasedLocking = new TestBiasedLocking(); list.add(testBiasedLocking); // 新建鎖對象 synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加鎖-線程1"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); // 打印對象頭信息 } } }, "線程1").start(); // 讓線程1跑一下子 Thread.sleep(2000); // 線程2 new Thread(() -> { for (int i = 0; i < 30; i++) { TestBiasedLocking testBiasedLocking = list.get(i); synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加鎖-線程2"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); // 打印對象頭信息 } } }, "線程2").start(); LockSupport.park(); } }
先解釋下是什麼意思,這是輸出了三次對象信息,第一行就是對象頭信息(分爲4組,每組8位,查看方式爲,組(左-右),位(右-左)),紅框內的最後三位就是最低的三位,即 偏向鎖(1bit)+鎖標誌(2bit),其餘研究下就明白了;
1)【線程1】1-50次加鎖都是101,即偏向鎖狀態;這個沒什麼問題,線程1首次加的鎖,而且沒有別的線程競爭,因此對象頭是偏向鎖狀態,對應的Thread Id爲線程1. 2)【線程2】1-19加的鎖都是輕量級鎖,即前19次進行了偏向鎖撤銷,第20次執行了重偏向,線程id指向線程2;
舉個不太形象的例子。好比你結婚了,你就屬於你老公的了,可是隔壁老王看上你了(線程2加鎖),這個時候你是不能和他在一塊兒的(不可重偏向),除非離婚(鎖消除,升級爲輕量級鎖,不可再結婚,偏向鎖不可逆);有一天,zf統計發現某個地區離婚太頻繁了,直接給他們一次換老公的機會(可重偏向),前提是這個機會只給老公不在家的(正在使用的鎖對象已經屬於正在使用的線程);
下面咱們把例子帶入那個數據中,當第19我的離婚後,zf直接給要離婚的人一次換人的機會,因此第20我的就直接把結婚證上的人名換成老王了(Tread Id直接指向線程2)
後來zf發現,換過老公的這批人,離婚仍是那麼頻繁(重偏向過的鎖對象頻繁 撤銷偏向鎖),搞我呢?都玩完!這個地區都別給我結婚了之後(該類下的鎖對象都不支持偏向鎖了,正在運行的也撤銷)。這就是批量撤銷的含義,咱們代碼來演示下blog
public class TestBiasedLocking { public static void main(String[] args) throws DecoderException, InterruptedException { // 首先咱們建立一個list,來存放鎖對象 List<TestBiasedLocking> list = new LinkedList<>(); // 線程1 new Thread(() -> { for (int i = 0; i < 50; i++) { TestBiasedLocking testBiasedLocking = new TestBiasedLocking(); list.add(testBiasedLocking); // 新建鎖對象 synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加鎖-線程1"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); } } LockSupport.park(); }, "線程1").start(); // 讓線程1跑一下子 Thread.sleep(2000); // 線程2 new Thread(() -> { for (int i = 0; i < 40; i++) { TestBiasedLocking testBiasedLocking = list.get(i); synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加鎖-線程2"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); } } LockSupport.park(); }, "線程2").start(); // 讓線程2跑一下子 Thread.sleep(2000); // 線程3 new Thread(() -> { for (int i = 20; i < 40; i++) { TestBiasedLocking testBiasedLocking = list.get(i); synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加鎖-線程3"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); } } LockSupport.park(); }, "線程3").start(); // 讓線程3跑一下子 Thread.sleep(2000); System.out.println("新出生的妹子"); System.out.println(ClassLayout.parseInstance(new TestBiasedLocking()).toPrintable()); LockSupport.park(); } }
下面咱們用通俗的語言來解釋下rem
1) 線程一、1-50都結婚了(有偏向的線程了) 2) 線程二、1-19都離婚了(鎖撤銷),zf看不行啊,直接結婚證更名吧,20-40都換老公了(Thread Id直接換了) // 到達BiasedLockingBulkRebiasThreshold次數 3) 線程三、20-40又離婚了(鎖撤銷),滾蛋,都玩完,誰都不能結婚,結婚的也都給我離婚 (設置爲不可偏向狀態,正在運行的鎖對象會被撤銷)// 到達BiasedLockingBulkRevokeThreshold次數 4) 新出生的妹子,生下來就不讓結婚。(new出來就是輕量級鎖)
二婚的人持續時間超過-XX:BiasedLockingDecayTime=25000ms的話,撤銷次數清爲0,從新計算。
最後我來解釋下文章中出現的舉例get
怎麼判斷只有一次換老公的機會的(每一個對象是怎麼判斷只有重偏向的一次機會的?)
答:對象頭中偏向鎖有個Epoch,該對象對應的Class中也有這個字段,當可偏向的時候,Class中的Epoch+1,正在使用的鎖對象Epoch也會+1(老公在家,沒有機會換老公),判斷可重偏向的時候,Class.Epoch != 對象.Epoch,表明可重偏向。
歡迎指正。