第二章:五、轻量级锁的原理与实战
2023-11-04 15:11 阿方技术圈 阅读(234) 评论(0) 收藏 举报引入轻量级锁的主要目的是在多线程竞争不激烈的情况下,通过CAS机制竞争锁减少重量级锁产生的性能损耗。重量级锁使用了操作系统底层的互斥锁(Mutex Lock),会导致线程在用户态和核心态之间频繁切换,从而带来较大的性能损耗。
轻量锁存在的目的是尽可能不动用操作系统层面的互斥锁,因为其性能比较差。线程的阻塞和唤醒需要CPU从用户态转为核心态,频繁地阻塞和唤醒对CPU来说是一件负担很重的工作。同时我们可以发现,很多对象锁的锁定状态只会持续很短的一段时间,例如整数的自加操作,在很短的时间内阻塞并唤醒线程显然不值得,为此引入了轻量级锁。轻量级锁是一种自旋锁,因为JVM本身就是一个应用,所以希望在应用层面上通过自旋解决线程同步问题。
轻量级锁的执行过程:在抢锁线程进入临界区之前,如果内置锁(临界区的同步对象)没有被锁定,JVM首先将在抢锁线程的栈帧中建立一个锁记录(Lock Record),用于存储对象目前Mark Word的拷贝,这时的线程堆栈与内置锁对象头大致如图2-11所示

然后抢锁线程将使用CAS自旋操作,尝试将内置锁对象头的Mark Word的ptr_to_lock_record(锁记录指针)更新为抢锁线程栈帧中锁记录的地址,如果这个更新执行成功了,这个线程就拥有了这个对象锁。然后JVM将Mark Word中的lock标记位改为00(轻量级锁标志),即表示该对象处于轻量级锁状态。抢锁成功之后,JVM会将Mark Word中原来的锁对象信息(如哈希码等)保存在抢锁线程锁记录的Displaced Mark Word(可以理解为放错地方的Mark Word)字段中,再将抢锁线程中锁记录的owner指针指向锁对象。在轻量级锁抢占成功之后,锁记录和对象头的状态如图2-12所示。

package com.heima.multiThread;
import org.junit.Test;
public class BiasedLockTest {
private static int count = 0;
@Test
public void testLightLock(){
final BiasedLockTest demo = new BiasedLockTest();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 100000; j++) {
demo.increment();
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + count);
}
public synchronized void increment() {
count++;
}
}
浙公网安备 33010602011771号