问题:什么是Monitor?

​ –解答:Monitor译为监视器,也叫管程。是一个同步工具,基于操作系统中的互斥锁实现。可以抽象为如下结构

image-20240315122058095

问题:synchronized关键字是如何实现锁机制的?

​ –解答:java对象存储在堆中,具体来说由3部分组成:对象头、对象体和对齐字节。其中对象头中的Mark Word标记字(8字节)用来表示当前线程的锁状态。

image-20240315125951203

1
2
3
4
//临界区
synchronized(Object o){

}

最开始,没有线程访问临界区,因此对象o的Mark Word是以下状态(后面会讲到,jvm做了优化,一开始锁对象其实是偏向锁的状态,偏向的是主线程)

image-20240315125855032

表明这是一个正常对象(即无锁状态),此时,来了一个线程并且需要访问临界区,此时锁会升级为偏向锁,对象o的Mark Word的状态发生改变

image-20240315130215486

当一个线程访问临界区代码块并获取锁时,会在Mark Word里存储锁偏向的线程ID。在线程进入和退出临界区时不再通过CAS操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的线程ID。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可。

如果此时又来了另一个线程,并且也要访问临界区,锁会升级为轻量级锁。成功获取锁的线程会在自己的栈帧中创建一个Lock Recoder的空间,会将锁对象的Mark Word中的内容复制到该空间中,而Mark Word中会存放指向该空间的指针。如果该操作失败,JVM会查看锁对象的Mark Word是否指向当前线程的栈帧,如果是说明当前线程已经获得了锁,可以进入临界区。

image-20240315134645348

当资源竞争更加激烈时,锁对象会升级为重量级锁。锁对象的Mark Word会指向一个Monitor对象,成功获取该锁的线程会被存放在Monitor的owner中,对于获取锁失败的线程都会进入EntryList中陷入阻塞状态。

image-20240315135926975

image-20240315135950638

问题:什么是轻量级锁,什么是重量级锁?两者有什么区别?

​ –解答:轻量级锁是将锁对象和线程绑定起来,轻量级锁主要用于线程分开访问临界区的情况;而重量级锁将锁对象和Monitor对象绑定,对于获取锁失败的线程直接进入自旋,如果失败进入阻塞状态,可以解决所有情况下的线程安全问题,但是性能差。轻量级锁可以升级为重量级锁

问题:什么是锁膨胀?发生在什么情况下?这个过程是怎样的?

​ –解答:指锁升级。这个过程对用户来说是透明的,且过程不可逆。当锁已不能满足线程间并发访问资源时,就会发生锁膨胀。

问题:什么是自旋优化?有什么作用?

​ –解答:自旋优化是锁的一种优化策略,指的是当线程获取锁失败时,不会立即进入阻塞状态,而是稍微等待一下再重试,重试几次都失败后再进入阻塞状态。可以减少唤醒操作,提高性能。

问题:什么是偏向锁?有什么作用?

​ –解答:如上所述,偏向锁是一种非常低级的锁,主要用于只有一个线程访问临界区的情况,使用偏向锁不需要进行检查和CAS操作,性能好。默认情况下,每个对象的初始状态都是偏向锁状态,而不是无锁状态。

问题:为什么调用了对象的hashCode方法后,会撤销偏向锁?而轻量级锁以及重量级锁无影响?

​ –解答:这和偏向锁实现的机制有关,当一个对象的状态变为偏向锁时,会将偏向线程的id存放在Mark Word的前54位中(见上图)。这54位有一部分原本是用来存放hashcode的值,但是在java中有一种特性:只有你调用了对象的hashcode方法后,这部分区域存放的才真正是hashcode的值,其他时候这部分存放的都是0,有点类似懒加载。因此当你调用了对象的hashcode后,就没有地方存放线程id了,自然就无法转换为偏向锁了。相较于偏向锁,轻量级锁和重量级锁在更换Mark Word中的内容之前,都会找一个容器存放起来,因此调用对象的hashcode方法时,依然可以转换为轻量级锁以及重量级锁。

问题:什么是批量重偏向?有什么作用?

​ –解答:这是jvm中的一种优化策略,当同类型的锁偏向失败发生了19次后,第20次以及之后的偏向失败都会进行重偏向,锁对象会更改偏向线程,而不是升级为轻量级锁。

问题:什么是批量撤销?发生在什么情况下?

​ –解答:当同类型的锁偏向失败39次时,第40次及之后的同类型锁对象都会撤销偏向锁状态(包括新创建的同类型的锁)

问题:撤销之后会发生什么?

​ 解答:按照正常的流程走,只是不能转换为偏向锁而已,可以升级为轻量级锁和重量级锁,只不过相较于偏向锁,性能较低。

preview

关于java中的锁可以参考以下文章:

java - 偏向锁的【批量重偏向与批量撤销】机制 - 个人文章 - SegmentFault 思否

史上最全java中锁的总结! - 知乎 (zhihu.com)

Java对象结构与锁实现原理及MarkWord详解_java打印锁持有者-CSDN博客