前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发基石-Markword与锁升级

并发基石-Markword与锁升级

作者头像
歪歪梯
发布2020-06-19 16:19:34
5742
发布2020-06-19 16:19:34
举报
文章被收录于专栏:歪歪梯Club

synchronized

synchronized关键字是java提供的互斥锁关键字,我们常说的互斥锁一般都是非自旋锁,即竞争不到锁的线程会进入阻塞状态知道被唤醒 今天我们来讲讲java中用来对synchronized进行优化的三种锁,同时会介绍markword对象头 目前我在网上搜到的十几篇博客讲的都有问题,可能有写对的我没搜到. 很多人不经过验证直接把markOop.hpp中的JavaThread*当成ThreadId这是错误的,实际是java线程在C语言的指针 并且未计算过hashCode和计算过hashCode的情况也是不一样的 本篇博客最后会展示使用jol工具,读取展示对象头的结果进行验证 附上openjdk的markOop.hpp链接

对象头Markword

对象头是java将对象比较常用和重要的运行时数据,如hashCode、gc标识、存活年龄等等进行集中存储的一组数据 占据8个字节,以下只讨论目前比较常见的64位的情况

偏向锁、轻量级锁、重量级锁介绍

64bit下各锁状态的Markword格式

代码语言:javascript
复制
64 bits:
  --------
  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
  size:64 ----------------------------------------------------->| (CMS free block)

  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
 [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

32bit下各锁状态的Markword格式

代码语言:javascript
复制
32 bits:
  --------
  hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
  JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
  size:32 ------------------------------------------>| (CMS free block)
  PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

也就是对象头的最后两位,是作为锁的状态标志 00---轻量锁 01---偏向锁/无锁 10---重量锁 11---GC标记 偏向锁状态和无锁状态通过区分对象头倒数第三位来确定,0代表无锁,1代表偏向锁 注意,未计算hashCode的对象的初始状态为匿名偏向锁(线程指针为0,代表无线程获取)而非无锁

偏向锁与无锁

java中对于锁的实际获取依赖于UnSafe调用native方法去实现不同操作系统上的cas原语操作 如果一个对象,在每次作业的运行始终处于单一线程,那每次对于锁的检测、获取和释放都会对性能造成不小的消耗 于是java引入了偏向锁 当一个处于匿名偏向锁状态的对象,第一次被一个线程竞争时,其对象头会被标记为偏向锁,同时存储其线程指针 接下来每次该线程对该锁的获取都不需要经过不必要的cas判断锁资源从而优化了性能,这也是偏向的由来

匿名偏向锁状态的对象如果计算了hashCode,则会变为无锁状态。hashCode存在markword,并且接下来不会再进去偏向锁

匿名偏向锁状态的对象被获取时,进入非匿名偏向锁状态,markword存储持有者的java线程在操作系统的C语言指针

无锁状态下的对象被获取时,会直接跳到轻量级锁(因为偏向锁下markword没有记录hashCode,没办法存储hashCode,而轻量级锁的下面讲)

非匿名偏向锁状态的对象计算了hashCode以后,会直接进入重量级锁,此时的重量级锁会(重量级锁的下面讲)

开启偏向锁的支持需要添加两个虚拟机参数

代码语言:javascript
复制
-XX:+UseBiasedLocking
-XX:BiasedLockingStartupDelay=0

第一个参数是开启偏向锁 第二个参数是指定立即开启,因为默认偏向锁的开启时在虚拟机运行后延时5秒 如果没有线程竞争,非匿名偏向锁偏向锁释放后会变回匿名偏向锁状态

锁升级-轻量级锁

当一个对象处于非匿名偏向锁状态下,如果有别的线程过来竞争,另一个线程尝试竞争锁,竞争失败并给予jvm一个竞争的信号以后进入自旋(不断尝试获取锁) 接下来在持有该锁的线程执行来到安全点时,会触发stop the world并将膨胀为轻量级锁 轻量级锁会创建一份锁记录(Lock Record)在当前持有他的线程的线程栈里 LockRecord中包含一个owner属性指向锁对象,而锁对象的markword中也会保存一个执行该LockRecord的指针

这时的竞争就是轻量级锁的竞争了,轻量级锁的竞争时,竞争锁的线程会在一个周期时间内不断的自旋获取锁,如果获取失败就会进入阻塞并将markword的锁标记标记为10(重量级锁)

因为LockRecord复制了markword,所以在执行同步块时并不去关注markword,只有到了释放时

1.锁对象的markword升级为了重量级锁,将锁对象升级为重量级锁,锁对象的markword存储一个指向一个由操作系统实现的mutex互斥变量,唤醒阻塞的竞争线程

2.markword没变化,释放锁,对象锁恢复到无锁状态(如果LockRecord记录的是偏向锁,则恢复到匿名偏向锁,否则恢复到无锁状态)

锁升级-重量级锁

当对象来到重量级锁以后,新被从竞争队列挑选出来一部分竞争锁的线程队列会一起竞争锁 最终竞争到锁的一个线程会继续运行,竞争失败的线程进入阻塞队列 处于执行状态的线程执行完同步代码块后,会释放锁并唤醒阻塞队列中的线程 将他们加入新的挑选出来的竞争锁的线程队列,并重新竞争锁,重复以上操作 因为需要阻塞和唤醒线程,所以需要从用户态到系统态切换,所以重量级锁下的系统开销很大

代码验证

代码语言:javascript
复制
        Thread currentThread = Thread.currentThread();
        System.out.println("threadId : "+Long.toBinaryString(currentThread.getId()));
        Object o = new Object();
        System.out.println("-------------------------------------------------------------------------------------------------------");
        System.out.println("init object info");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");
        synchronized (o) {
            System.out.println("synchronized lock object info");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
            System.out.println("synchronized finished");
            System.out.println("-------------------------------------------------------------------------------------------------------");
        }
        Thread.sleep(2000);
        System.out.println("after synchronized object info");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("binary hashCode : "+Integer.toBinaryString(o.hashCode()));
        System.out.println("-------------------------------------------------------------------------------------------------------");

        System.out.println("after calculate hashcode object info");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");

        synchronized (o) {
            System.out.println("synchronized lock object info");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
            System.out.println("synchronized finished");
            System.out.println("-------------------------------------------------------------------------------------------------------");
        }
        Object o2 = new Object();
        System.out.println("o2 hashCode : "+Long.toBinaryString(o2.hashCode()));
        System.out.println("init lock object2 info");
        System.out.println(ClassLayout.parseInstance(o2).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");
        synchronized (o2) {
            System.out.println("synchronized lock object2 info");
            System.out.println(ClassLayout.parseInstance(o2).toPrintable());
            System.out.println("synchronized finished");
            System.out.println("-------------------------------------------------------------------------------------------------------");
        }
        System.out.println("after lock object2 info");
        System.out.println(ClassLayout.parseInstance(o2).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");
        //计算过hashcode的会直接进入轻量锁

输出结果

代码语言:javascript
复制
threadId : 1
-------------------------------------------------------------------------------------------------------
init object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
synchronized lock object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 e8 09 01 (00000101 11101000 00001001 00000001) (17426437)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
after synchronized object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 e8 09 01 (00000101 11101000 00001001 00000001) (17426437)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

binary hashCode : 100000111110100010001111000001
-------------------------------------------------------------------------------------------------------
after calculate hashcode object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 c1 23 fa (00000001 11000001 00100011 11111010) (-98320127)
      4     4        (object header)                           20 00 00 00 (00100000 00000000 00000000 00000000) (32)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
synchronized lock object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           50 f7 c4 02 (01010000 11110111 11000100 00000010) (46462800)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
o2 hashCode : 110101100000011100010111110011
init lock object2 info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 f3 c5 81 (00000001 11110011 11000101 10000001) (-2117733631)
      4     4        (object header)                           35 00 00 00 (00110101 00000000 00000000 00000000) (53)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
synchronized lock object2 info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           50 f7 c4 02 (01010000 11110111 11000100 00000010) (46462800)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
after lock object2 info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 f3 c5 81 (00000001 11110011 11000101 10000001) (-2117733631)
      4     4        (object header)                           35 00 00 00 (00110101 00000000 00000000 00000000) (53)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

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

多线程竞争

代码语言:javascript
复制
Thread currentThread = Thread.currentThread();
        System.out.println("threadId : "+Long.toBinaryString(currentThread.getId()));
        Object o = new Object();
        System.out.println("-------------------------------------------------------------------------------------------------------");
        System.out.println("init object info");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");

        new Thread() {
            public void run() {
                synchronized (o) {
                    System.out.println("-------------------------------------------------------------------------------------------------------");
                    System.out.println("-------------------------------------------------------------------------------------------------------");
                    System.out.println("another thread id "+Long.toBinaryString(this.getId()));
                    System.out.println("synchronized lock object info");
                    System.out.println(ClassLayout.parseInstance(o).toPrintable());
                    System.out.println("synchronized finished");
                    System.out.println("-------------------------------------------------------------------------------------------------------");
                    System.out.println("------------------------------------------------------------------------------------------------------");
                }
            }
        }.start();

        synchronized (o) {
            System.out.println("synchronized lock object info");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());

            //有锁未计算hashCode状态下计算hashCode

            o.hashCode();
            System.out.println("when synchronized calculate hashcode ");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());

            System.out.println("synchronized finished");
            System.out.println("-------------------------------------------------------------------------------------------------------");
        }
        Thread.sleep(2000);
        System.out.println("after synchronized object info");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("binary hashCode : "+Integer.toBinaryString(o.hashCode()));
        System.out.println("-------------------------------------------------------------------------------------------------------");

        System.out.println("after calculate hashcode object info");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");

        synchronized (o) {
            System.out.println("synchronized lock object info");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
            System.out.println("synchronized finished");
            System.out.println("-------------------------------------------------------------------------------------------------------");
        }
        Object o2 = new Object();
        System.out.println("o2 hashCode : "+Long.toBinaryString(o2.hashCode()));
        System.out.println("init lock object2 info");
        System.out.println(ClassLayout.parseInstance(o2).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");
        synchronized (o2) {
            System.out.println("synchronized lock object2 info");
            System.out.println(ClassLayout.parseInstance(o2).toPrintable());
            System.out.println("synchronized finished");
            System.out.println("-------------------------------------------------------------------------------------------------------");
        }
        System.out.println("after lock object2 info");
        System.out.println(ClassLayout.parseInstance(o2).toPrintable());
        System.out.println("-------------------------------------------------------------------------------------------------------");
        //计算过hashcode的会直接进入轻量锁

输出结果

代码语言:javascript
复制
threadId : 1
-------------------------------------------------------------------------------------------------------
init object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
synchronized lock object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ba c4 c4 02 (10111010 11000100 11000100 00000010) (46449850)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

when synchronized calculate hashcode 
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ba c4 c4 02 (10111010 11000100 11000100 00000010) (46449850)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------
another thread id 1010
synchronized lock object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ba c4 c4 02 (10111010 11000100 11000100 00000010) (46449850)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
after synchronized object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 c1 23 fa (00000001 11000001 00100011 11111010) (-98320127)
      4     4        (object header)                           20 00 00 00 (00100000 00000000 00000000 00000000) (32)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

binary hashCode : 100000111110100010001111000001
-------------------------------------------------------------------------------------------------------
after calculate hashcode object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 c1 23 fa (00000001 11000001 00100011 11111010) (-98320127)
      4     4        (object header)                           20 00 00 00 (00100000 00000000 00000000 00000000) (32)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
synchronized lock object info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           90 f5 ad 02 (10010000 11110101 10101101 00000010) (44955024)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
o2 hashCode : 110101100000011100010111110011
init lock object2 info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 f3 c5 81 (00000001 11110011 11000101 10000001) (-2117733631)
      4     4        (object header)                           35 00 00 00 (00110101 00000000 00000000 00000000) (53)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
synchronized lock object2 info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           90 f5 ad 02 (10010000 11110101 10101101 00000010) (44955024)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

synchronized finished
-------------------------------------------------------------------------------------------------------
after lock object2 info
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 f3 c5 81 (00000001 11110011 11000101 10000001) (-2117733631)
      4     4        (object header)                           35 00 00 00 (00110101 00000000 00000000 00000000) (53)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

-------------------------------------------------------------------------------------------------------
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 歪歪梯Club 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • synchronized
  • 对象头Markword
  • 偏向锁、轻量级锁、重量级锁介绍
    • 偏向锁与无锁
      • 锁升级-轻量级锁
      • 锁升级-重量级锁
      • 代码验证
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档