并发基石-Markword与锁升级

java 专栏收录该内容
21 篇文章 0 订阅

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格式

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格式

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以后,会直接进入重量级锁,此时的重量级锁会(重量级锁的下面讲)

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

-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记录的是偏向锁,则恢复到匿名偏向锁,否则恢复到无锁状态)

锁升级-重量级锁

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

代码验证

		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的会直接进入轻量锁

输出结果

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

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

多线程竞争

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的会直接进入轻量锁

输出结果

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

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

更多文章,请搜索公众号歪歪梯Club
更多资料,请搜索公众号编程宝可梦

  • 1
    点赞
  • 1
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值