synchronized 底层如何实现?什么是锁升级、降级?

synchronized 代码块是由一对 monitorenter/monitorexit 指令实现的,Monitor 对象是同步的基本实现单元。

  • https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#d5e13622

在Java6之前, Monitor的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作。现代的( Oracle)JDK中,JVM对此进行了大刀阔斧地改进,提供了三种不同的 Monitor 实现,也就是常说的三种不同的锁:偏斜锁( Biased Locking)轻量级锁重量级锁,大大改进了其性能。

什么是锁升级,降级?

所谓的锁升级、降级,就是 JVM 优化 synchronized 运行的机制,当 JVM 监测到不同的竞争状况是,会自动切换到不同的锁实现。这种切换就是锁的升级、降级。

对象的结构

说偏向锁之前,需要理解对象的结构,对象由多部分构成的,对象头,属性字段、补齐区域等。所谓补齐区域是指如果对象总大小不是4字节的整数倍,会填充上一段内存地址使之成为整数倍。

偏向锁又和对象头密切相关,对象头这部分在对象的最前端,包含两部分或者三部分:Mark Words、Klass Words,如果对象是一个数组,那么还可能包含第三部分:数组的长度。

  • Klass Word里面存的是一个地址,占32位或64位,是一个指向当前对象所属于的类的地址,可以通过这个地址获取到它的元数据信息。
  • Mark Word需要重点说一下,这里面主要包含对象的哈希值、年龄分代、hashcode、锁标志位等。

如果应用的对象过多,使用64位的指针将浪费大量内存。64位的JVM比32位的JVM多耗费50%的内存。 我们现在使用的64位 JVM会默认使用选项 +UseCompressedOops 开启指针压缩,将指针压缩至32位。

以64位操作系统为例,对象头存储内容图例

|--------------------------------------------------------------------------------------------------------------|
|                                              Object Header (128 bits)                                        |
|--------------------------------------------------------------------------------------------------------------|
|                        Mark Word (64 bits)                                    |      Klass Word (64 bits)    |
|--------------------------------------------------------------------------------------------------------------|
|  unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |     OOP to metadata object   |  无锁
|----------------------------------------------------------------------|--------|------------------------------|
|  thread:54 |         epoch:2      | unused:1 | age:4 | biased_lock:1 | lock:2 |     OOP to metadata object   |  偏向锁
|----------------------------------------------------------------------|--------|------------------------------|
|                     ptr_to_lock_record:62                            | lock:2 |     OOP to metadata object   |  轻量锁
|----------------------------------------------------------------------|--------|------------------------------|
|                     ptr_to_heavyweight_monitor:62                    | lock:2 |     OOP to metadata object   |  重量锁
|----------------------------------------------------------------------|--------|------------------------------|
|                                                                      | lock:2 |     OOP to metadata object   |    GC
|--------------------------------------------------------------------------------------------------------------|

对象头中的信息如何理解呢,举个例子

从该对象头中分析加锁信息,MarkWordk为0x0000700009b96910,二进制为0xb00000000 00000000 01111111 11110000 11001000 00000000 01010011 11101010。 倒数第三位为"0",说明不是偏向锁状态,倒数两位为"10",因此,是重量级锁状态,那么前面62位就是指向互斥量的指针。

basied_locklock状态001无锁101偏向锁000轻量级锁010重量级锁011GC标记

  • age:Java GC标记位对象年龄。
  • identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中。
  • thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念。
  • epoch:偏向时间戳。
  • ptr_to_lock_record:指向栈中锁记录的指针。
  • ptr_to_heavyweight_monitor:指向线程Monitor的指针。

无锁

A a = new A();System.out.println(ClassLayout.parseInstance(a).toPrintable());

可以看到最后 00000001 basied_lock = 0, lock =01 表示无锁

JavaThread.synchronizestructure.A object internals:OFFSET  SIZE      TYPE DESCRIPTION                               VALUE0     4           (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4           (object header)                           43 c0 00 20 (01000011 11000000 00000000 00100000) (536920131)12     1   boolean A.flag                                    false13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

偏斜锁

当没有竞争出现时,默认使用偏斜锁。 JVM 会利用 CAS 操作在对象头上的 Mark Word 部分设置线程 ID ,以表示对象偏向当前线程。所以并不涉及真正的互斥锁,这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竟争开销。 测试代码:

Thread.sleep(5000);
A a = new A();
synchronized (a) {System.out.println(ClassLayout.parseInstance(a).toPrintable());
}

运行结果:

JavaThread.synchronizestructure.A object internals:OFFSET  SIZE      TYPE DESCRIPTION                               VALUE0     4           (object header)                           05 28 8d 02 (00000101 00101000 10001101 00000010) (42805253)4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4           (object header)                           43 c0 00 20 (01000011 11000000 00000000 00100000) (536920131)12     1   boolean A.flag                                    false13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

00000101 中 basied_lock = 1, lock =01 表示偏斜锁

轻量级锁

thread1中依旧输出偏向锁,主线程获取对象A时,thread1虽然已经退出同步代码块,但主线程和thread1仍然为锁的交替竞争关系。故此时主线程输出结果为轻量级锁。 测试代码:

Thread.sleep(5000);
A a = new A();Thread thread1= new Thread(){@Overridepublic void run() {synchronized (a){System.out.println("thread1 locking");System.out.println(ClassLayout.parseInstance(a).toPrintable()); //偏向锁}}
};
thread1.start();
thread1.join();
Thread.sleep(10000);synchronized (a){System.out.println("main locking");System.out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
}
}

运行结果:

JavaThread.synchronizestructure.A object internals:OFFSET  SIZE      TYPE DESCRIPTION                               VALUE0     4           (object header)                           05 40 e9 19 (00000101 01000000 11101001 00011001) (434716677)4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4           (object header)                           a0 c0 00 20 (10100000 11000000 00000000 00100000) (536920224)12     1   boolean A.flag                                    false13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes totalmain locking
JavaThread.synchronizestructure.A object internals:OFFSET  SIZE      TYPE DESCRIPTION                               VALUE0     4           (object header)                           38 f6 c7 02 (00111000 11110110 11000111 00000010) (46659128)4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4           (object header)                           a0 c0 00 20 (10100000 11000000 00000000 00100000) (536920224)12     1   boolean A.flag                                    false13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

00000101 依然是偏向锁,00111000 是轻量级锁

重量级锁

thread1 和 thread2 同时竞争对象a,此时输出结果为重量级锁

测试代码:

Thread.sleep(5000);
A a = new A();
Thread thread1 = new Thread(){@Overridepublic void run() {synchronized (a){System.out.println("thread1 locking");System.out.println(ClassLayout.parseInstance(a).toPrintable());try {//让线程晚点儿死亡,造成锁的竞争Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
};
Thread thread2 = new Thread(){@Overridepublic void run() {synchronized (a){System.out.println("thread2 locking");System.out.println(ClassLayout.parseInstance(a).toPrintable());try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
};
thread1.start();
thread2.start();

运行结果:

thread2 locking
JavaThread.synchronizestructure.A object internals:OFFSET  SIZE      TYPE DESCRIPTION                               VALUE0     4           (object header)                           7a f5 99 17 (01111010 11110101 10011001 00010111) (395965818)4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4           (object header)                           62 c1 00 20 (01100010 11000001 00000000 00100000) (536920418)12     1   boolean A.flag                                    false13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

01111010 basied_lock = 0 lock=10 重量级锁

synchronized 底层如何实现?什么是锁升级、降级?相关推荐

  1. 【synchronized底层原理之4】锁的升级过程及比较

    2019独角兽企业重金招聘Python工程师标准>>> 参考 https://blog.csdn.net/zqz_zqz/article/details/70233767 https ...

  2. Synchronized锁升级底层原理

    思考问题 首先请您思考下面的问题: Synchronized锁同步机制性能不好嘛? 一个对象天生对应一个monitor锁吗? 为什么说synchronized是非公平锁? synchronized字节 ...

  3. 学习笔记day12 synchronized底层实现及锁升级机制

    原博客:https://blog.csdn.net/weixin_40394952/article/details/118693945 一.synchronized使用方法 1.修饰实例方法,对当前实 ...

  4. 面试官:说一下Synchronized底层实现,锁升级的具体过程?

    介绍 这是我去年7,8月份面试的时候被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问 ...

  5. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    一.重量级锁 上篇文章中向大家介绍了Synchronized的用法及其实现的原理.现在我们应该知道,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本质 ...

  6. 死磕Synchronized底层实现--偏向锁

    注:本篇很长,请找个舒适的姿势阅读. 本文为synchronized系列第二篇.主要内容为分析偏向锁的实现. 偏向锁的诞生背景和基本原理在上文中已经讲过了,强烈建议在有看过上篇文章的基础下阅读本文. ...

  7. Synchronized关键字和锁升级

    一.Synchronized 对于多线程不安全(当数据共享(临界资源),而多线程同时访问并改变该数据时,就会不安全),JAVA提供的锁有两个,一个是synchronized关键字,另外一个就是lock ...

  8. Java并发编程—Synchronized底层优化(偏向锁、轻量级锁)

    原文作者:Matrix海 子 原文地址:Java并发编程:Synchronized底层优化(偏向锁.轻量级锁) 目录 一.重量级锁 二.轻量级锁 三.偏向锁 四.其他优化 五.总结 一.重量级锁 上篇 ...

  9. yield方法释放锁吗_死磕Synchronized底层实现重量级锁

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:farmerjohngit 链接:https://github.com/farmer ...

最新文章

  1. 圆头像 微信小程序 绘图_小程序canvas绘制圆形微信头像
  2. 告诉你KVC的一切-b
  3. Pycharm安装PyQT5调用QTDesigner
  4. ajax跨域请求问题总结
  5. Python----Requests库基本使用
  6. MDT2012+ADK8.0+WDS部署Windows客户端(一)部署概念和方法论
  7. VMware虚拟机里centos7下安装mysql5.6并授权远程连接Navicat
  8. 2021年SWPUACM暑假集训day1二分算法
  9. mybatis 原理_了解Mybatis的工作原理吗
  10. python怎么用for循环找出最大值_从“for in”循环中获取最小值和最大值
  11. Inceptor导出建表语句、存储过程
  12. 计算机英语词汇带音标,常用计算机英语词汇3000例(含音标)
  13. 安全HCIP之eSight
  14. 实验七 数据选择器和译码器的应用
  15. php setlocale 中国,PHP setlocale无效
  16. 在路上●我的年轻●勇往直前●匆匆十年
  17. 洛谷P2240木材加工
  18. PhpSpreadsheet导入
  19. 操作Excel导入的问题(转)
  20. HTML(二) -- 表格设计

热门文章

  1. JavaScript学习随记——面向对象编程(继承)
  2. NYOJ 6 喷水装置(一)
  3. Opencv一维直方图的绘制
  4. java enummap_Java EnumMap size()方法与示例
  5. python sep函数_Python中带有print()函数的sep参数
  6. 在Python中使用OpenCV(CV2)对图像进行边缘检测
  7. C++---汉明距离
  8. A+B Problem(V)
  9. 装配图位置偏转怎么调整_物理微课|匀变速直线运动、电容器动态分析及磁偏转技巧、方法、模型...
  10. uva 138——Street Numbers