相关文章:
锁状态位之无锁
锁状态位之偏向锁
锁状态位之轻量级锁
锁状态位之重量级锁
第一次验证
我们创建一个对象,没有上锁,然后再上锁,来观察锁标识位:
由于只有一个线程加锁,没有产生,所以预期是加偏向锁
public class Test {
public static void main(String[] args) throws InterruptedException {
//TimeUnit.SECONDS.sleep(5); 代码很重要,先注释掉,后面需要放开
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable()); //第一次打印,此时无锁
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable()); //第二次打印,此时有锁
}
}
}
执行结果:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 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) e5 01 f3 27 (11100101 00000001 11110011 00100111) (670237157)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 88 f8 98 02 (10001000 11111000 10011000 00000010) (43579528)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 f3 27 (11100101 00000001 11110011 00100111) (670237157)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Process finished with exit code 0
分析:
第一条是001无锁的,和锁状态位之无锁中第一章节内容相同,主要看第二条打印:
注意字节是反序的:88 f8 98 02 (10001000第四个字节 11111000第三个字节 10011000第二个字节 00000010第一个字节)
我们发现第二条打印结果是轻量级锁
,不是预期的偏向锁
那么为什么呢?
虽然默认开启偏向锁,但JVM会延迟去启动偏向锁,延迟大约三四秒,此时对于系统的前几秒来说,等价于没有开启偏向锁,解决方案有2种:
- 加sleep 5s再试,放开代码中的语句块
TimeUnit.SECONDS.sleep(5);
- 关闭延迟开启偏向锁
-XX:BiasedLockingStartupDelay=0
对于第二条语句来说,等价于 无锁 ->加轻量级锁(单线程加锁,低竞争度)
第二次验证
加了延迟后,对于第二条语句来说,等价于 无锁 ->延迟3后启动偏向锁(偏向线程ID
为空) ->偏向锁,设置偏向线程ID
为当前线程id
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 21 f3 27 (11100101 00100001 11110011 00100111) (670245349)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 a8 5c 02 (00000101 10101000 01011100 00000010) (39626757)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 21 f3 27 (11100101 00100001 11110011 00100111) (670245349)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Process finished with exit code 0
第一条显示偏向锁,但是偏向线程ID
为空
jdk1.8时,默认开启偏向锁,创建一个对象的话(在真正开启偏向锁之后才创建的对象), 对象会是可偏向状态,即锁的标识显示是偏向锁,但是偏向线程ID为空,等到后续真的发生需要加偏向锁时,会仅修改偏向线程ID即可。
第二条显示偏向锁,偏向线程ID
为当前线程id
总结
对于单线程,低竞争度的锁来说,不是一定加轻量级锁,而是偏向锁的+线程id 来过度的,当然,需要jvm一定时间去开启,需要等待几秒钟
参考:
《禁止延迟开启偏向锁jvm参数》