《java并发编程艺术》笔记——synchronized的实现原理与应用

时间:2021-09-09 15:56:53

本文为《java并发编程艺术》第2.2章synchronized的实现原理与应用的笔记

InfoQ上的文章地址


为何偏向锁撤销时需要暂停线程

偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程

当读到这里的时候,有点疑惑,为何撤销锁的时候还需要特地停止拥有锁的线程?
首先要明白的是,什么是偏向锁

当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要花费CAS操作来加锁和解锁,而只需简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁,如果测试成功,表示线程已经获得了锁

偏向锁其实只是一个简单的标记,在需要同步的对象的头中标记哪个线程获得了锁,再通俗的说,对象头只是标记了哪个线程能够操作它。这种简单的标记使得判断线程是否获得了锁十分简便,只是一个简单的判断
《java并发编程艺术》笔记——synchronized的实现原理与应用

但是由于过于简单,没有任何其他信息,连线程是否执行完了同步块(释放锁)都无法知道,所以当偏向锁需要进行调度时(如由其他线程获得偏向锁)需要额外操作,如当前获得偏向锁的线程是否活着,而这个操作是判断线程某一点的状态,如果不停止线程,很可能在宣布线程还活着的时候,线程早已结束了

轻量级锁获取/释放图解

轻量级锁单单从文字上去理解,感觉很难,所以画出图辅助理解

轻量级锁加锁:线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

《java并发编程艺术》笔记——synchronized的实现原理与应用

轻量级锁解锁:轻量级解锁时,会使用原子的CAS操作来将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

《java并发编程艺术》笔记——synchronized的实现原理与应用

synchronized为何要三种锁状态

synchronized的偏向锁,轻量级锁与重量级锁分别解决三种情况:
1. 只有一个线程进入临界区
2. 多个线程交替进入临界区
3. 多线程同时进入临界区

其实只需要一种方案,重量级锁,就可以解决上述三种情况,但是对于一个复杂的问题,一个通用的解决方案往往是不高效的,往往是一个特定的情况下,需要某个特定的解决方案才能达到最优。
当解决一个复杂的问题时有多种方案下犹豫不决,或是某个方案对于问题的解决效率不高时,可以选择像synchronized一样,划分出问题可以能会达到的状态,然后分别找出其对应的最优解决方案,再来进行动态的方案抉择

轻量级锁膨胀为重量级锁

轻量级锁膨胀为重量级锁的过程其实并不是太过于明确,这里可以参考一下知乎的一个问题
jvm从轻量级锁膨胀到重量级锁是在什么时候发生的?