多线程中 synchronized 锁升级的原理是什么?

synchronized 作用

  • 确保线程互斥的访问同步代码
  • 保证共享变量的修改能够及时可见
  • 有效解决重排序问题

synchronized 用法

  • 修饰普通方法
  • 修饰静态方法
  • 修饰代码块

 synchronized 底层实现原理

  • 同步代码块是通过 monitorenter 和 monitorexit 指令获取线程的执行权
  • 同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制

多线程中 synchronized 锁升级的原理是什么?

从JDK1.5开始并发包引入了Lock锁,Lock锁是基于Java实现的。因此锁的获取和释放都是由java代码实现的。然而synchronized是基于操作系统底层的Mutex Lock来实现的。每次获取释放锁都会带来用户态和内核态的切换。

作用:减少用户态和内核态之间的切换,这种切换是非常消耗cpu性能的。

在JDK1.6开始,对象实例在堆中会被划分三个组成部分:对象头,实例数据,和对其填充。

  • 实例数据:对象的相关属性
  • 对其填充:确保数据长度一致。有些没有数据的自动填充一些空间。

对象头
     Mark Word
    指向类的指针
     数组长度
我们在锁升级的过程中只需要关注Mark World(它记录了对象,锁和垃圾回收相关的信息,在64位JVM中其长度为64bit)的位信息包括了如下组成信息:
    - 无锁标记: 当前对象没有上锁。
     1. 偏向锁标记:
     2. 轻量级锁标记:
     3. 重量级锁标记:直接从用户态切换到内核态。
     4. GC标记: 判断对象是否可被垃圾收回收掉。

  对于synchronized锁来说,锁的升级主要是通过Mark World的锁标记位与是否是偏向锁标志来达成的。synchronized关键字所对应的锁都是从偏向锁开始的,随着锁竞争的不断升级逐步演化至轻量级锁,最后则变成重量级锁。
 

这位大牛 介绍很清楚

 

       重点介绍了JDk中采用轻量级锁和偏向锁等对Synchronized的优化,但是这两种锁也不是完全没缺点的,比如竞争比较激烈的时候,不但无法提升效率,反而会降低效率,因为多了一个锁升级的过程,这个时候就需要通过-XX:-UseBiasedLocking来禁用偏向锁。下面是这几种锁的对比:

多线程中 synchronized 锁升级的原理是什么?

 

synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。

 

锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。