上一节,就着montior把重量级锁的内容写了一下,这节写一下轻量级锁。
轻量级锁与重量级锁场景
首先按场景划分一下:
1.使用重量级锁的场景,在一个线程持有锁资源来正在进行业务操作时,另有其它线程在操作未执行完成时去竞争当前锁资源,使用重量级锁。(锁膨胀,后期说一下)。
2.使用轻量级锁的场景,在一个线程执行完成之后,释放了锁资源,此时另外的线程在这个线程结束后获取到释放的锁资源来进行操作时,使用了轻量级锁。(从偏向锁升级为轻量级锁)。
3.使用偏向锁的场景,这个也说一下吧。在一个线程执行完成后,释放了锁资源,此时没有其它线程与当前线程竞争资源,只有当前线程一直复用这个锁资源。那么当下一次该线程获取到这个锁对象时,会与对象头中mark word中的ThreadId来进行比较,如果相同的情况下,直接获取到该锁资源。(按可重入锁的例子来看一下,就知道了)。
可重入代码展示
在这里引入了jol包的依赖信息
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
<!-- <scope>provided</scope>-->
</dependency>
代码如下:
package com.bo.threadstudy.four;
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;
/**
* 轻量级锁示例
*/
@Slf4j
public class LightWeightTest {
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
String s = "";
//00000001 无锁(偏向锁有延迟,所以是无锁)
getStatus();
synchronized (lock){
//刚进入锁的状态,通过jol打印对象头
getStatus();
//11111000轻量级锁
log.debug(s);
log.debug("第一次进入锁");
method();
}
//00000001无锁
getStatus();
}
private static void method() {
synchronized (lock){
//10001000轻量级锁
getStatus();
log.debug("进入可重入锁");
}
}
private static void getStatus(){
String s = ClassLayout.parseInstance(lock).toPrintable();
log.debug(s);
}
}
暂时先不探讨偏向锁的问题。
getStatus()方法打印出了对象头信息,从顺序上来看:
1.在进入synchronized前是无锁状态
2.进入synchronized是轻量级锁状态
3.再次进入可重入锁第二个synchronized也是轻量级锁状态
4.从synchronized中出来后就又变成无锁状态了。
解释一下具体流程吧。
轻量级锁流程
1.在每一个Thread中,都包含了一个专门记录锁记录(Lock Record)的结构,在竞争锁成功后,来用以存储锁资源的mark word信息。(初始状态下存放的是自己的地址)
2. 在获取锁成功后,使用Object reference指向锁对象的地址,并使用CAS将锁资源的mark word记录到锁记录对象中,锁记录的地址以及00(轻量级锁状态)存放至锁资源的mark word中。
3.替换成功了,现在也就是加锁成功了,此时锁资源中记录了锁记录地址以及锁状态00,表示现在已经加锁成功了。
如果cas替换失败的话,需要考虑两种情况。
第一种情况,是其它线程来竞争获取当前锁资源,此时发生锁膨胀,将升级为重量级锁。
第二种情况,是当前线程自身来获取这个锁(比如锁重入),此时在当前线程中将再次创建一个lock Record。
其实我个人目前觉得上面的场景并不是太全,后面写代码的时候好好捋捋。
4.当退出代码块synchronized解锁时如果发现取值存在有null值的记录,代表当前锁记录是重入锁的记录。这时重置锁记录,表示锁重入记录减1(这个后期看看ReetrantLock的源码,看看这个是怎么解决的,synchronized有点太底层了)。
5.在退出锁时,发现锁记录中的内容不为null,这时就采用CAS将锁记录中的值与锁资源中的值来进行替换,此时又是两种场景:
CAS替换成功,代表了解锁成功
CAS替换失败,则代表了该轻量级锁升级成了重量级锁,markword中的值的格式也不一样了,此时就需要按照重量级锁的方式来进行解锁了。