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