轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以 使用轻量级锁来优化。

轻量级锁对使用者是透明的,即语法仍然是synchronized

java中synchronized默认是轻量级锁,但当有线程在同一时间发生锁竞争时,轻量级锁会升级为重量级锁Monitor

假设有两个方法同步块,利用同一个对象加锁

static final Object lock = new Object();
public static void method1() {
  synchronized( lock ) {
    // 同步块 A
    method2();
  }
}
public static void method2() {
  synchronized( lock ) {
 	 // 同步块 B
  }
}

以下为个人理解:

  • 当方法1先进入锁执行的时候,会将自己的 锁l地址和 lock对象的地址值进行一次交换,轻量级锁的地址值后两位为00 , lock对象的地址值为hashcode计算的值
  • 当单个线程执行时,方法2也进入锁执行的时候,因为是同一个线程,所以开辟的内存空间是一样的,它不会再进行地址值的交换, 此时记录的地址值为null ,并且对lock Record进行加1,作为重入的计数
  • 当退出synchronize 代码块(解锁时)如果有取值为null的锁记录,表示有重入,这时重置锁记录,表示重入计数-1 (减去一条lock record)

以下为专业解释:

  • method1 执行到synchronized( lock ) 创建锁记录(Lock Record)对象的,每个线程的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word
  • 让锁记录的对象指针(Object reference)指向锁对象,并尝试用 cas 替换 Object 的 Mark Word ,将Mark Word的值存入锁记录(01 表示无锁状态 [01 还有偏向锁状态] 、00 轻量级锁状态)
  • 如果 cas 替换成功 ,对象头中存储了 锁记录的地址 和状态 00,表示由改线程给对象加锁了

java重量锁与轻量锁 轻量锁升级为重量锁_加锁

 

  • 如果cas 失败了 则有2种情况

1.如果其他线程已经持有以了该Object的轻量级锁,这时表明有锁竞争,进入锁膨胀的过程(可看锁膨胀图解)

2.如果是自己执行了synchronize锁重入(method2 执行到synchronized( lock ) ),那么再添加一条lock Record作为重入的计数

java重量锁与轻量锁 轻量锁升级为重量锁_开发语言_02

 

        当退出synchronize 代码块(解锁时)如果有取值为null的锁记录,表示有重入,这时重置锁记录,表示重入计数-1 (减去一条lock record)

java重量锁与轻量锁 轻量锁升级为重量锁_java重量锁与轻量锁_03

 

  • 当退出synchronize 代码块(解锁时)锁记录的值不为null,这时使用cas将Mark Word 的值恢复给对象头
  • 成功 : 解锁成功
  • 失败:说明轻量级锁进行了锁膨胀或者已经升级为重量级锁,进入重量级锁流程