文章目录
- 1、轻量级锁
- 1.1、加锁
- 1.2、解锁
- 2、重量级锁
- 2.1、加锁和解锁
- 2.2、自旋优化
1、轻量级锁
1.1、加锁
- 使用场景:如果一个对象虽然有多个线程访问,但是多线程访问时间是错开的,即多线程之间不会发生竞争。
- 轻量级锁对使用者是透明的,即语法仍然是synchronized
如下代码,2个同步方法,利用同一个对象加锁:
static final Object obj = new Object();
public static void method1() {
synchronized(obj) {
// 同步方法A
}
}
public static void method2() {
synchronized(obj) {
// 同步方法B
}
}
轻量级锁加锁过程解析:
- 线程和对象的初始状态,图示:
- 创建锁记录(Lock Record)对象,每一个线程的栈帧都会包含一个锁记录的结构,内部用来存储锁对象的Mark Word 以及锁对象的地址。
- 当线程Thread-0执行到synchronized同步代码块的时,把锁记录中Object Reference(指针)执行锁对象,并尝试用cas替换Object 的MarkWord,把MarkWord的值存入锁记录,图示:
- cas简单理解为原子操作,执行过程中不会被打断
- 轻量级锁MardWord结构:ptr_to_lock_record:30 00
- 如果cas替换成功,对象头中存储了锁记录地址和状态 00,表示由该线程加锁,图示:
- cas替换失败有两种情况
- 如果有其他线程早于当前线程成功加锁,即Object MarkWord后2位一置为00,此时发生锁膨胀,升级为重量级锁
- 如果是自己执行了synchronized锁重入,那么在添加一条Lock Record做为重入的计数,图示:
1.2、解锁
解锁过程分析:
- 当线程退出syncronized代码块时,如果有取值为null的锁记录,表示由重入,这时重置锁记录,表示重入此时减一
- 当线程退出syncronized代码块时,如果锁记录的值不为null,这时使用cas把MarkWord的值恢复为最初的值(即保存在锁记录中的值)
- 解锁成功
- 解锁失败,说明轻量级锁进行了锁膨胀或者已经升级为重量级锁,进入重量级锁解锁过程。
2.1、加锁和解锁
如果在尝试加轻量级锁的过程中,CAS操作无法成功,其中一种情况就是有其他线程给该对象已经加上轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。
当Thread-1进行轻量级锁加锁时,Thread-0已经给该对象加了轻量级锁。
这时Thread-1加轻量级锁失败,进入锁膨胀流程
- 即为Object 对象申请Monitor锁,让Object指向Monitor地址
- 然后自己进入Monitor的EntryList Blocked
- 图示,
解锁:当Thread-0退出同步块解锁时,使用cas将MarkWord的值恢复给对象头,失败。这时,会进入重量级解锁流程,即按照Monitor地址找到Monitor对象,设置Owner值为null,唤醒EntryList中阻塞的线程。
2.2、自旋优化
重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即持锁线程已经退出同步块,释放了锁),这是当前线程就可以避免阻塞。
自旋成功的情况
线程1(cup 1) | 对象Mark | 线程2(cup 2) |
访问同步块,获取monitor | 10(重量级锁) | - |
成功(加锁) | 10(重量级锁) | - |
执行同步块 | 10(重量级锁) | 访问同步块,获取monitor |
退出同步块(解锁) | 01(无锁) | 自旋重试 |
- | 10(重量级锁) | 成功加锁 |
- | 10(重量级锁) | 执行同步块 |
- | 01(无锁) | 退出同步块(解锁) |
… | … | … |
自旋失败的情况
线程1(cup 1) | 对象Mark | 线程2(cup 2) |
访问同步块,获取monitor | 10(重量级锁) | - |
成功(加锁) | 10(重量级锁) | - |
执行同步块 | 10(重量级锁) | 访问同步块,获取monitor |
执行同步块 | 10(重量级锁) | 自旋重试 |
执行同步块 | 10(重量级锁) | 自旋重试 |
执行同步块 | 10(重量级锁) | 自旋重试 |
执行同步块 | 10(重量级锁) | 阻塞 |
… | … | … |
- Java6之后自旋锁是自适应,比如对象刚刚一次自旋操作成功过,那么这次自旋成功的可能性会高,就多自旋几次;反之,就减少自旋次数。
- 自旋会占用CPU时间,单核CPU自旋就是浪费,多核CPU自旋才能发挥优势。
- Java 7之后不能控制是否开启自旋功能。