> 文章列表 > Synchronized锁升级之无锁和偏向锁

Synchronized锁升级之无锁和偏向锁

Synchronized锁升级之无锁和偏向锁

为了优化synchronized锁的效率,在JDK1.6中,HotsPot虚拟机开发团队提出了锁升级的概念,包括偏向锁、轻量级锁、重量级锁,锁的升级值的是 无锁态 >> 偏向锁 >> 轻量级锁 >> 重量级锁

synchronized同步锁相关信息保存在锁对象的对象同中的Mark Word中,锁升级功能主要是依赖Mark Word中锁的标准为何是否偏向锁来实现的
Synchronized锁升级之无锁和偏向锁

从上图我们可以看到,无锁态对应的锁标志位位 01,是否偏向锁标志位 0,下面通过两个简单的Java case来演示无锁态和轻量级锁

因为要查看代码运行时的内存布局分析,所以在构建的maven工程中需要加入如下依赖

<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version>
</dependency>

1.无锁态

下面用一段代码来演示什么是无锁态

  1. Java code
public class Main {public static void main(String[] args) {Object objLock = new Object();// 需要注意,只有调用了hashCode(),对象头中的MarkWord才会保存对应的hashCode值,否则全部是0System.out.println("10进制: " + objLock.hashCode());System.out.println("2进制: " + Integer.toBinaryString(objLock.hashCode()));System.out.println("16进制: " + Integer.toHexString(objLock.hashCode()));System.out.println(ClassLayout.parseInstance(objLock).toPrintable());}
}
  1. 代码运行时输出的结果
    Synchronized锁升级之无锁和偏向锁

  2. 分析

Mark Word对象头共站8个字符,共64位,也就是上图中的 1 ~ 8,从后往前拼起来

00000000 00000000 00000000 01100111 01110011 00100111 10110110 00000001

Mark Word在64位JVM中内部结构中

  1. 红色字体部分:25位,不使用
  2. 蓝色字体部分:31位,表示对象的hashCode(可以看到与代码输出结构中的二进制hashCode是一样的)
  3. 橙色字体部分:1位,不使用
  4. 绿色字体部分:4位,表示对象的分代年龄(对象的最大分代年龄是15,占用的四个字节的最大是 1111 ,将其换成10进制即是 15)
  5. 紫色字体部分:1位,偏向锁标准为(0:不是,1:是)
  6. 黄色字体部分:2位,锁的标志位,按照上图中的这里是无锁态(001无锁态,101偏向锁,000轻量级锁,010重量级锁,011GC标识)

2.偏向锁

注意事项

偏向锁的启用时机,偏向锁在程序运行时需要进行 4s 的加载,如果程序在运行时,线程还没有去访问锁前程序的运行时间已经超过了4s,那么此时这把锁将会从无锁态升级到偏向锁

2.1.偏向锁的原理

  1. 在偏向锁第一次被线程拥有的时候,在偏向锁的Mark Word中,有一块区域用来记录偏向锁的ID,如下图所示
    Synchronized锁升级之无锁和偏向锁

  2. 偏向锁的好处是什么?

    1. 假设现在的锁对象已经是一个偏向锁,偏向锁它会偏向于第一个获取锁的线程,并且在锁对象的Mark Word中会将该线程的线程ID保存起来,当该线程再来访问锁的时候,会先判断当前访问的线程ID是否是Mark Word中记录的线程ID,如果是将会不进行加锁,该线程直接执行同步块中的代码,这样省去了CAS去更新对象头的操作,提升了锁的性能
  3. 偏向锁再次进入同步代码块,如果保证不需要再次加锁?

2.2.偏向锁的撤销

注意事项

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁

在大部分情况下,锁都是被同一个线程获取到的,持有偏向锁的线程不会主动释放锁。那么大部分情况下不会涉及到偏向锁的撤销,当有另外的线程尝试竞争偏向锁的时候,这个时候才会涉及偏向锁的撤销流程

暂时无法在飞书文档外展示此内容

2.3.Java case

public class Main {public static void main(String[] args) throws InterruptedException {// 在这里sleep 5s,等待偏向锁加载成功Thread.sleep(6000);Object objLock = new Object();synchronized (objLock){System.out.println(ClassLayout.parseInstance(objLock).toPrintable());}}
}

Synchronized锁升级之无锁和偏向锁