一、轻量级锁
轻量级锁的使用场景:如果一个对象虽然有多线程访问,但多线程访问的时间错开(也就是没有竞争),那么可以使用轻量级锁来优化。
轻量级锁对使用者是透明的,即语法仍然是synchronized。
假设有两个方法同步块,利用同一个对象加锁
static final Object obj=new Object();
public static void method1(){
synchronized (obj){
//同步块A
method2();
}
}
public static void method2(){
synchronized (obj){
//同步块B
}
}
创建锁记录(Lock Record)对象,不像重量级锁是Monitor对象作为锁,轻量级锁是Lock Record作为轻量级锁,每个线程的栈帧中都会包含一个锁记录的结构(如本例中就是method1方法对应的栈帧中创建Lock Record对象),内部可以存储锁定对象的Mark Word,其中Hashcode Age Bias 01是Object的Mark Word部分,记录对象当前的状态以及是否被加锁,如果是01代表对象是normal状态,即未被加锁。
让锁记录中Object reference指向锁对象,并尝试用cas替换Object中的Mark Word,将Mark Word的值存入锁记录。
如果cas替换成功,对象头中存储了锁记录地址和状态00,表示由该线程给对象加锁,这时图示如下,00代表该对象被加上轻量级锁。对象Mark Word中的原信息存储在Lock Record中等待解锁的时候恢复。
cas替换失败有两种情况
-如果其他线程已经持有了该Object的轻量级锁(即发现Object的Mark Word的状态码是00),这时表示有竞争,进入锁膨胀过程。<br /> -如果是自己(当前线程继续对该对象加锁)执行了synchronized锁重入,那么再添加一条Lock Record作为重入的计数。<br /> <br /> 如本例的情况即为在同一线程内再次synchronized(Obj),会继续对Object上轻量级锁,即在当前线程内再创建一个Lock Record对象,但是Lock Record地址设为null值,仅仅作为重入计数。
当退出synchronized代码块(解锁时)如果有取值为null的锁记录,表示有重入,Lock Record直接清除掉即可。
当退出synchronized代码块(解锁时)的记录值不为null,这时使用cas将Mark Word的值恢复给对象头
-成功,则解锁成功<br /> -失败,说明轻量级锁j进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程。
注意:
- 重量级锁,obj才会和一个monitor对象关联。轻量级锁,是线程内创建的Lock Record与Obj相关联。
- 每执行一次synchronized,就会在栈帧内创建一个Lock Record锁记录对象,Object reference也会指向要加锁的对象,并尝试cas交换内容,虽然可能会交换不成功,即Object已经被其它线程上锁,但Lock Record依旧会被创建。
二、锁膨胀
如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时代表其它线程在为此对象加上了轻量级锁(有竞争),而轻量级锁是无竞争状态的锁,所以涉及到竞争的情况,需要进行锁膨胀,将轻量级锁膨胀为重量级锁(Monitor锁)
static Object obj = new Object();
public static void main(){
synchronized(obj){
//同步块
}
}
当Thread-1进行轻量级加锁时,Thread-0已经对该对象加了轻量级锁
这时Thread-1加轻量级锁失败,进入锁膨胀流程。注意当Thread-1执行到synchronized(obj)时,依然会尝试为obj加锁,即在线程的方法对应的栈帧中创建Lock Recored对象,Object reference也依旧会指向Object对象,但当执行cas操作时,发现object对象已经被其它线程加上了轻量级锁,此时进入锁膨胀。
<br /> 流程为:<br /> -为object对象申请Monitor锁,让object指向重量级锁地址<br /> -然后自己进入Monitor的EntryList 【BLOCKED】 <br /> 
当Thread-0退出同步块解锁时,由于Thread-0持有的(注意持有的说法,先给obj上锁,然后只有拿到锁才能执行同步代码块的内容)是轻量级锁,所以释放锁时按照轻量级锁释放流程,使用cas将Mark Word的值恢复给对象头,失败。因为此时object中的Mark Word已经不再是Thread-0的Lock Record地址,而是Monitor的地址,所以无法将Hashcode等信息替换到Mark Word字段中。这时则需要进入重量级锁的解锁流程,即按照Monitor的地址找到Monitor对象,然后将Lock Record中的obj的Hashcode Age等信息存入Monitor中,注意这一步是必须的,否则锁释放完毕后无法恢复obj的头信息,最后设置Owner为null,唤醒EntryList中【BLOCKED】线程即可。