JAVA併發-驗證sychronized鎖升級、降級

前言:
也許有些東西不去驗證不去探索,終究還只是資料,變不成知識,人生漫漫,見到的很多,知道得很少,精力有限,做個知道的人,哪怕知道那麼一點也可以.

先貼一張synchronized鎖圖,下面通過實驗來驗證下該圖描述是否正確。
大家熟知的一張圖
相關信息:
1.主要工具:jdk自帶HSDB工具,HSDB具體是啥,我百度下:HSDB則是在SA基礎上包裝起來的一個調試器,至於SA是啥,再百度下:是個非常便於探索HotSpot VM內部實現的API,再說下個人理解,使用HSDB可以看到對象頭信息,這個很重要! 我的實驗也是基於此。
具體如何使用HSDB:參考這個文章
2.2.synchronized鎖相關知識:參考文章 1, 2
3.關於對象頭信息,可以看下openjdk源碼中markOop.hpp文件的註釋信息,這裏只摘出關於對象頭中mark標記位的鎖相關注釋:

// [JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread
// [0 | epoch | age | 1 | 01] lock is anonymously biased
//
// - the two lock bits are used to describe three states: locked/unlocked and monitor.
//
// [ptr | 00] locked ptr points to real header on stack
// [header | 0 | 01] unlocked regular object header
// [ptr | 10] monitor inflated lock (header is wapped out)
// [ptr | 11] marked used by markSweep to mark an object not valid at any other time

4.關於線程切換耗性能,可以參考linux下官方的一篇文章

一、測試單線程下,獲取鎖後,鎖信息

測試代碼:

/**
 * @Author: ingore1992
 * @Description: 驗證單線程下 synchronized獲取到鎖後,查看鎖狀態是否是偏向鎖
 * @Date: Created In 15:52 2019/1/22
 */
public class SynchronizedTest1 {

    public static void main(String[] args)throws Exception{
        LockObject entity = new LockObject();
        //主線程先休息兩分鐘,方便我們通過HSDB工具類來查看線程棧中對象信息
        Thread.sleep(60000 * 2L);
        synchronized (entity){
            System.out.println("主線程獲取到鎖");
            try {
                //持有鎖幾分鐘
                Thread.sleep(60000 * 2L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主線程獲釋放鎖");
        }

        System.out.println("主線程多休息一會,方便等待鎖降級.");
        Thread.sleep(60000 * 100L);
    }
}

步驟:
1.啓動main函數,命令行下執行jps,找到對應的線程id
2.啓動HSDB,命令行下,cd到jdk的lib目錄,執行:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
3.使用HSDB圖型化工具attach到線程id
查找鎖對象信息
在這裏插入圖片描述
(主線程還沒開始獲取鎖還在休眠中…)查看鎖對象頭信息
在這裏插入圖片描述
(主線程獲取到對象鎖)查看鎖對象頭信息
在這裏插入圖片描述
將mark_word字段信息轉換成2進制
在這裏插入圖片描述
(經歷很長很長一段時間)查看鎖對象
在這裏插入圖片描述
分析:
1.主線程休眠時還沒去獲取鎖,此時mark_word爲1,即01,無鎖狀態
2.主線程獲取到鎖,此時mark_word轉換爲2進制後,後兩位爲00,輕量級鎖狀態
3.主線程經過一段長時間的休眠,mark_word變爲1,即01,無鎖狀態
總結:單線程下,獲取到鎖是輕量級鎖,等待一段時間後,鎖恢復到無鎖狀態

二、測試多線程下,競爭鎖,鎖信息

測試代碼:

/**
 * @Author: ingore1992
 * @Description: 驗證多線程下 線程競爭鎖時間較長,鎖會升級到重量級鎖,鎖競爭結束一段時間後,鎖降級。
 * @Date: Created In 15:44 2019/1/22
 */
public class SynchronizedTest {

    public static void main(String[] args)throws Exception{
        LockObject entity = new LockObject();
        Thread t1 = new Thread(()->{
            synchronized (entity){
                System.out.println("線程1獲取到鎖");
                try {
                    //持有鎖幾分鐘
                    Thread.sleep(60000 * 2L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程1獲釋放鎖");
            }
        });

        Thread t2 = new Thread(()->
        {
            synchronized (entity){
                System.out.println("線程2獲取到鎖");
                try {
                    Thread.sleep(60000 * 2L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程2釋放鎖");
            }
        });
        t1.start();
        t2.start();

        System.out.println("主線程多休息一會,方便等待鎖降級.");
        Thread.sleep(60000 * 100L);
    }
}

查找鎖對象信息
在這裏插入圖片描述
(線程競爭,且時間較長)查看鎖對象頭信息
在這裏插入圖片描述
將mark_word字段信息轉換成2進制
在這裏插入圖片描述
分析:
1.一個子線程獲取到鎖,另一個子線程去競爭鎖,且時間較長,此時mark_word轉換爲2進制後,後兩位爲10,重量級鎖狀態。
2.後面缺張圖,經過一段休眠後,鎖也恢復到了無鎖狀態
3.多線程下,競爭鎖時間較長,鎖會升級到重量級鎖,中間經歷了自旋,這個不太好驗證,如果有網友可以驗證自旋,希望告知

github:https://github.com/ingorewho/do-test/tree/master/synchronized-test

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章