锁膨胀过程针对JDK1.6之后
无锁->偏向锁
偏向锁只适用于线程没有竞争的情况。对象一开始被创建出来,Mark Word最后3位101则是支持偏向锁的状态(默认初始创建4秒后开启支持)
加锁过程
升级为偏向锁的过程:
1.检查Mark Word是否为101(支持偏向锁)
2.检查对象的Mark Word中存的线程ID是当前线程ID,如果是的话则表示当前线程已经持有了该偏向锁(偏向锁不会主动释放),则直接执行代码块。
3.如果对象的Mark Word中存的线程ID不是当前线程ID,则尝试CAS改为当前线程ID
4.若第3步中CAS修改成功,则获取偏向锁成功,执行同步代码块
5.若第3步中CAS修改失败,则进入撤销偏向锁(偏向锁不会主动释放,直到其他线程来竞争),等待进入安全点
释放锁过程
1.等待进入安全点(在这个是时间点上没有字节码正在执行)
2.暂停拥有偏向锁的线程,检查持有偏向锁的线程是否还处于同步块当中,如果已经退出同步块则将对象设置为无锁状态,否则将锁升级为轻量级锁
3.恢复原持偏向锁的线程从安全点继续执行

偏向锁->轻量级锁
轻量级锁即自旋锁,一般用于多线程交替执行而非竞争的情况下
注意:如果一个线程在加锁时发现锁对象的Mark Word已经改为了00,则不会再尝试从偏向锁开始,而是直接加轻量级锁
加锁过程
1.在线程的栈帧中创建Lock Record记录(拷贝一份Mark Word对象头)
2.尝试CAS将对象的Mark Word改为00(轻量级锁状态),并且Lock Record指向当前线程创建的Lock Record
3.如果第2步CAS成功,则获取到轻量级锁,执行同步代码块
4.如果第2步CAS失败,有两种情况:
(1)别的线程已经持有该锁,此时(自旋几次,达到一定次数没有成功才?)升级为重量级锁
(2)已经指向了当前线程的Lock Record,说明发生了锁的重入,则新创建一个Lock Record,内容为null。(后续释放轻量级锁的时候一定是Lock Record为非null才行)
解锁过程
1.退出synchronized代码块,此时Lock Record不为null(非重入的)
2.使用CAS将Lock Record的值恢复给对象头,并将Lock Record弹出
3.如果第2步CAS失败,则说明已经升级为了重量级锁,进入重量级锁的解锁过程
3.如果第2步CAS成功,则解锁成功
如下面图片展示:

创建Lock Record(对象Mark Word拷贝)

CAS替换尝试

CAS替换成功,轻量级锁加锁成功

CAS替换失败,重入的情况,新建一个null的lock record
轻量级锁->重量级锁
重量级锁应用于多线程竞争的场景
重量级锁即将轻量级锁中的Lock Record替换为了Monitor
升级过程
1.当Thread-1进行轻量级锁加锁时,Thread-0已经对该对象加了轻量级锁

2.这时Thread-1加轻量级锁失败,进入锁膨胀过程,为锁对象Object申请Monitor锁,让Object的Mark Word指向Monitor的地址,Owner指向当前持有轻量级锁的线程(Thread-0),然后让自己进入Monitor的EntryList中阻塞。

解锁过程
通过Object中Mark Word记录的Monitor地址找到Monitor对象,设置Owner为null,唤醒EntryList中的线程
Synchronized重量级锁原理
Monitor对象时JVM层面实现,主要关注:
(1)WaitSet:等待队列
(2)EntryList:阻塞(同步)队列
(3)Owner:当前持有锁的线程
指令层面转化为了MonitorEnter和MonitorExit指令