synchronized 原理进阶

通过本文档你将学习到

  • 轻量级锁
  • 锁膨胀
  • 自选优化
  • 偏向锁
  • 锁消除

接上一话,我们想个办法,不能一说送快递,你直接选择东风快递是吧,一加锁你直接使用重量级锁。我们想个办法,有时候没必要直接用东风快递,可以用跑腿对吧。那下面就开始介绍下:锁的优化过程

1、前置知识

《深入理解java虚拟机》的书中知道,我们可以通过分析Java对象头中MarkWord来查看是那种锁。


image.png

Java的对象头在对象的不同的状态下会有不同的表现形式,主要有三种状态,无锁状态,加锁状态,GC标记状态。那么就可以理解Java当中的上锁其实可以理解给对象上锁。也就是改变对象头的状态,如果上锁成功则进入同步代码块。但是Java当中的锁又分为很多种,从上图可以看出大体分为偏向锁、轻量锁、重量锁三种锁状态。这三种锁的效率是完全不同

关于对象状态一共分为五种状态,分别是无锁、偏向锁、轻量锁、重量锁、GC标记,但是2bit只能表示4种状态(00,01,10,11)JVM的做法将偏向锁和无锁的状态表示为同一个状态,然后根据图中偏向锁的标识再去标识是无锁还是偏向锁状态。
01 无锁 (010)
01偏向锁(101)
00轻量级锁
10重量级锁

这四个锁是什么意思,MarkWord什么鬼,MarkWord就是一个对象的一部分,里面存了一些信息,锁的问题下面会慢慢介绍

Java的对象头存储的是什么?我们可以看下


image.png

假设我们理解一个对象头主要由上图两个部分组成(数组对象除外,数组对象的对象还包含一个数组长度)

2、轻量级锁

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

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

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

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

public static void method2() {
synchronized( obj ) {
    // 同步块 B
     }
}

创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word


image.png

让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存
入锁记录


image.png

如果 cas 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下

image.png

如果 cas 失败,有两种情况
如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程
如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数

image.png

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

image.png

当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象

成功,则解锁成功
失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

2 锁膨胀
如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

static Object obj = new Object();
public static void method1() {
synchronized( obj ) {
// 同步块
 }
}

当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁


image.png

这时 Thread-1 加轻量级锁失败,进入锁膨胀流程
即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址
然后自己进入 Monitor 的 EntryList BLOCKED

image.png

当 Thread-0 退出同步块解锁时,使用 cas 将 Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁
流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程

你可能感兴趣的:(synchronized 原理进阶)