简介
轻量级锁又叫自旋锁,在用户态完成操作,没有涉及操作系统底层
示例代码
/**
* @author pangjian
* @ClassName LightWeightLocking
* @Description 轻量级锁加锁解锁过程示例代码
* @date 2021/11/1 10:55
*/
public class LightWeightLocking {
static final Object obj = new Object();
public static void main(String[] args) {
synchronized (obj) {
method();
}
}
public static void method() {
synchronized (obj) {
System.out.println("++++");
}
}
}
原理
加锁成功原理
- 当Thead-0执行到主方法的synchronized(obj)就会在栈帧中生成lock record(锁记录),内部可以存储锁定对象的mark word
- 让锁记录中Object reference指向锁对象,并尝试CAS替换Object的markword,将markword的值存入锁记录(暂存效果,解锁后还会把原来的hashcode,age信息替换回给Object)
- 如果CAS替换成功,Object的markword中存储了锁记录地址和状态00,表示由该线程给对象加上了锁
CAS失败和解锁原理
- 如果是其它线程已经持有了该Object的轻量级锁,这时表明有竞争,进入锁自旋过程
- 如果是自己执行了synchronized锁重入,那么再添加一条Lock Record作为重入的计数
当退出synchronized 代码块(解锁时)如果有取值为nul的锁记录,表示有重入,这时重置锁记录,表示重入计数减一,当退出synchronized代码块(解锁时)锁记录的值不为null,这时使用CAS将Mark Word 的值恢复给对象头,成功则解锁成功。也可能失败,就是因为其他线程也要对该Object对象进行CAS失败后导致锁自旋了(也就是说自旋锁),自旋失败后锁膨胀了,变成了重量级锁。
自旋和锁膨胀
如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时当前线程就会自旋(其实就是循环进行CAS操作),如果线程自旋到一定阈值或自旋线程数超cpu核心数的一定比例就会导致锁膨胀,将轻量级锁变为重量级锁。
- 线程1CAS加锁失败,进入锁自旋流程
- 自旋失败(有线程超过十次自旋或自旋线程数超过cpu核心数的一半)后为Object对象在操作系统中申请Monitor锁(lock record 地址 00 变成了monitor 地址 10),并让Object指向重量级锁地址
- 线程1进入monitor的EntryList(等待队列)
- 当Thread-O退出同步块解锁时,使用CAS将Mark Word的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照Monitor地址找到Monitor对象,设置Owner为null,唤醒EntryList中 BLOCKED线程
和重量级锁比较
如果临界区代码执行时间长,也就是同步代码量大和等待线程数大,就需要用重量级锁(悲观锁),因为转圈算法(轻量级锁)要占用cpu时间片,如果等待线程数多也会耗费大量资源,如果用排队算法(重量级锁),就不要cpu分配时间片