JAVA并发-验证sychronized锁升级、降级

前言:
也许有些东西不去验证不去探索,终究还只是资料,变不成知识,人生漫漫,见到的很多,知道得很少,精力有限,做个知道的人,哪怕知道那么一点也可以.

先贴一张synchronized锁图,下面通过实验来验证下该图描述是否正确。
JAVA并发-验证sychronized锁升级、降级_第1张图片
相关信息:
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
查找锁对象信息
JAVA并发-验证sychronized锁升级、降级_第2张图片
(主线程还没开始获取锁还在休眠中…)查看锁对象头信息
JAVA并发-验证sychronized锁升级、降级_第3张图片
(主线程获取到对象锁)查看锁对象头信息
JAVA并发-验证sychronized锁升级、降级_第4张图片
将mark_word字段信息转换成2进制
JAVA并发-验证sychronized锁升级、降级_第5张图片
(经历很长很长一段时间)查看锁对象
JAVA并发-验证sychronized锁升级、降级_第6张图片
分析:
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);
    }
}

查找锁对象信息
JAVA并发-验证sychronized锁升级、降级_第7张图片
(线程竞争,且时间较长)查看锁对象头信息
JAVA并发-验证sychronized锁升级、降级_第8张图片
将mark_word字段信息转换成2进制
JAVA并发-验证sychronized锁升级、降级_第9张图片
分析:
1.一个子线程获取到锁,另一个子线程去竞争锁,且时间较长,此时mark_word转换为2进制后,后两位为10,重量级锁状态。
2.后面缺张图,经过一段休眠后,锁也恢复到了无锁状态
3.多线程下,竞争锁时间较长,锁会升级到重量级锁,中间经历了自旋,这个不太好验证,如果有网友可以验证自旋,希望告知

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

你可能感兴趣的:(验证学习,java并发)