1、偏向锁

轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行cas操作。

java6中引入了偏向锁来做进一步优化:只有第一次使用cas将线程id设置到对象的Mark Word头,之后发现这个线程Id是自己的就表示没有竞争,不用重新cas。以后只要不发生竞争,这个对象就归该线程所有

例如:

static final Object obj = new Object();
public synchronized static void m1() {
}
public synchronized static void m2() {
}
public synchronized static void m3() {
}

轻量级锁:

java锁偏向锁如果进行重偏向 偏向锁 重入_多线程

偏向锁:

java锁偏向锁如果进行重偏向 偏向锁 重入_多线程_02

2、偏向状态

java锁偏向锁如果进行重偏向 偏向锁 重入_sed_03

一个对象创建时:

  • 如果开启了偏向锁(默认开启),那么对象创建后,markword值为0x05即最后3位为101,这时它的thread,epoch,age都为0。
  • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加VM参数-XX:BiasedLockingStartupDelay=0来禁用延迟。
  • 如果没有开启偏向锁,那么对象创建后,markword值为0x01即最后为001,这时它的hashcode、age都为0,第一次用到hashcode时才会赋值。
3、撤销

调用了对象的hashCode(),但偏向锁的对象MarkWord中存储的是线程id,如果调用hashCode会导致偏向锁被撤销

hashCode()//会禁用调用的这个对象的偏向锁
  • 轻量级锁会在锁记录中记录hashCode
  • 重量级锁会在Monitor中记录hashCode

在调用hashCode后使用偏向锁,记得去掉-XX:UseBiasedLocking//禁用偏向锁

4、批量重偏向

如果对象如果被多个线程访问,但没有竞争,这时偏向了线程T1的对象仍有机会重新偏向T2,重偏向会重置对象的Thread ID

当撤销偏向锁阀值超过20次后,jvm会这样觉得,我是不是偏向错了呢,于是会在给这些对象加锁时重新偏向至加锁线程

5、批量撤销

当撤销偏向锁阀值超过40次后,jvm会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的

6、锁消除

即时编译对锁的优化

下面两种优化后,性能差不多

java锁偏向锁如果进行重偏向 偏向锁 重入_java锁偏向锁如果进行重偏向_04

java锁偏向锁如果进行重偏向 偏向锁 重入_对象创建_05

因为x++在指令上已经是原子的了。所以在编译的时候,下面的代码去除了synchronized锁。