欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java中锁的四种状态以及锁升级过程

程序员文章站 2022-05-18 17:43:33
在Java(1.6+)中锁的状态一共有四种,级别由低到高分别是:无锁、偏向锁、轻量级锁、重量级锁,这几个状态会随着竞争情况逐渐升级,其中锁可以升级,但是不能降级。Java中加锁的最简单方式就是加synchronized关键字,那么为什么锁会有这么多状态的锁呢?在Java早期,synchronized叫做重量级锁,加锁过程需要操作系统在内核态访问核心资源,因此操作系统会在用户态与内核态之间切换,效率很低下。于是JDK1.6之后,JVM为了提高锁的获取与释放效率,对synchronized进行了优化,引入了偏...

在Java(1.6+)中锁的状态一共有四种,级别由低到高分别是:无锁、偏向锁、轻量级锁、重量级锁,这几个状态会随着竞争情况逐渐升级,其中锁可以升级,但是不能降级。Java中加锁的最简单方式就是加synchronized关键字,那么为什么锁会有这么多状态的锁呢?

在Java早期,synchronized叫做重量级锁,加锁过程需要操作系统在内核态访问核心资源,因此操作系统会在用户态与内核态之间切换,效率很低下。于是JDK1.6之后,JVM为了提高锁的获取与释放效率,对synchronized进行了优化,引入了偏向锁和轻量级锁,根据线程竞争情况对锁进行升级,在线程竞争不激烈的情况避免使用重量级锁。

对象头

在了解锁之前需要先了解一下对象头,我们都知道在Java中锁不是某一个具体的实物资源,而是对象上的某个标记,而这个标记就记录在对象头上。
Mark Word(对象头)是Java对象布局中的一个部分,关于Java对象布局可以参考我的另一篇博客:《Java对象的内存布局》,那么Mark Word内部是什么样子的呢?

在32位虚拟机中:
Java中锁的四种状态以及锁升级过程

在64位虚拟机中:
Java中锁的四种状态以及锁升级过程

由于现在计算机基本都是64位,所以下面以64位虚拟机为例,看一下锁具体是如何升级的
无锁:对象头中有31bit的空间来存储对象的hashcode,4bit用于存放对象分代年龄,1bit来表示是否是偏向锁,2bit存放锁标志位,偏向锁位与锁标志位合起来“001”就代表无锁。无锁就是没有对任何资源进行锁定,所有线程都能访问并修改资源。
偏向锁:对象头中记录了获得偏向锁的线程ID,偏向锁与锁标志位合起来“101”就代表偏向锁。有研究发现,在大多数情况下,锁很少被多个线程同时竞争,而且总是由同一个线程多次获得,因此只需要将获得锁的线程ID写入到锁对象Mark Word中,相当于告诉其他线程,这块资源已经被我占了。当线程访问资源结束后,不会主动释放偏向锁,当线程再次需要访问资源时,JVM就会通过Mark Word中记录的线程ID判断是否是当前线程,如果是,则继续访问资源。所以,在没有其他线程参与竞争时,锁就一直偏向被当前线程持有,当前线程就可以一直占用资源或者执行代码。
自旋锁:一旦有另外一个线程参与锁竞争,偏向锁就会升级为自旋锁,此时撤销偏向锁,锁标志位变为“00”。竞争的两个线程都在各自的线程栈帧中生成一个Lock Record空间,用于存储锁对象目前Mark Word的拷贝,用CAS操作将Mark Word设置为指向自己这个线程的LR(Lock Record)指针,设置成功者获得锁,其他参与竞争的线程如果未获取到锁,则会一直处于自旋等待的状态,直到竞争到锁。
重量级锁:长时间的自旋操作是很消耗CPU资源的,为了避免这种盲目的消耗,JVM会在有线程超过10次自旋,或者自旋次数超过CPU核数的一半(JDK1.6以后加入了自适应自旋-Adaptive Self Spinning,由JVM自己控制自旋次数)时,会升级到重量级锁。重量级锁底层是依赖操作系统的mutex互斥锁,也就是有操作系统来负责线程间的调度。重量级锁减少了自旋锁带来的CPU消耗,但是由于操作系统调度线程带来的线程阻塞会使程序响应速度变慢。

本文地址:https://blog.csdn.net/weixin_42105936/article/details/109608345

相关标签: java JVM