sychronized 锁升级

简介: sychronized 锁升级

sychronize有几种锁


sychronize


1.6之前,只存在重量级锁,也就是一个线程拿到锁之后,其他没有拿到锁的线程只能阻塞。


1.6之后,新加了偏向锁和轻量级锁(自旋锁)。


首先附上一张HotSpot虚拟机对象头Mark Word



HotSpot虚拟机对象的对象头部分包括两类信息。第一类是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它为“Mark Word”。对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的最大限度,但对象头里的信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个有着动态定义的数据结构,以便在极小的空间内存储尽量多的数据,根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机中,如对象未被同步锁锁定的状态下,Mark Word的32个比特存储空间中的25个比特用于存储对象哈希码,4个比特用于存储对象分代年龄,2个比特用于存储锁标志位,1个比特固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)如下图所示:



无锁


无锁是指没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。


也就是说,理想情况下,所有获取锁的线程都能够在第一次尝试的时候就成功,也就是没有真正发生竞争,此时,sychronized不会真正的加锁。


实际上不是这样。当对象刚被new出来的时候,没有线程进行访问,此时是无锁状态,一旦有线程来访问,其实第一个线程会加偏向锁。或者偏向锁撤销 对象头中的mark word 是空的时候 也是无锁状态。


也就是不存在多个线程访问,互相不冲突,大家维持友好的无锁状态这种事。


偏向锁


偏向锁是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗,即提高性能。


目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。


也就是说,此时也没有发生真正的竞争,可以理解为不存在第二个线程来获取锁,当对象被第一个线程访问,当锁对象第一次被线程获取的时候,虚拟机会把对象头中的标志位设为01、把偏向模式设置为1,表示进入偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时。虚拟机都可以不再进行任何同步操作(例如加锁、解锁以及对Mark Word的更新操作等)。


轻量级锁/自旋锁


轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋(关于自旋的介绍见文末)的形式尝试获取锁,线程不会阻塞,从而提高性能。


轻量级锁的获取主要由两种情况:① 当关闭偏向锁功能时;② 由于多个线程竞争偏向锁导致偏向锁升级为轻量级锁。


它设计的初衷是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。


也就是说,一旦发生了两个线程同时获取一把锁的情况,偏向锁就不合适了,会升级成轻量级锁,之所以称之为“轻量”,是因为其他没有获取到锁的线程并不会进入阻塞状态,而是会进入“自旋”,其实就是进入循环,循环内会尝试继续获取锁。


可以看一下偏向锁到轻量级锁的升级过程


  1. 在代码即将进入同步块的时候,如果此同步对象没有被锁定(锁标志位为01状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录的空间,用于存储锁对象目前的Mark Word拷贝(官方为这份拷贝加了个前缀Displaced)。


  1. 然后虚拟机将使用CAS操作尝试把对象的Mark Word更新为指向Lock Record的指针。如果这个更新动作成功了,即代表该线程拥有了这个对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后两个比特)将转变为00,表示此对象处于轻量级锁定状态。


  1. 如果这个操作失败了,那就意味着至少存在一条线程与当前线程竞争获取该对象的锁。虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧。如果是,说明当前线程已经拥有了这个对象的锁,那直接进入同步块继续执行就可以了,否则就说明这个锁对象已经被其他线程抢占了。当前线程会进行自旋。


  1. 如果出现两条以上的线程争用同一个锁,或者当前线程自旋失败(尝试到一定次数,默认10次)的情况,那轻量级锁就不再有效,必须要膨胀为重量级锁,锁标志的状态值变为10,此时Mark Word中存储的就是指向重量级锁监视器ObjectMonitor(互斥量)的指针,后面等待锁的线程也必须进入阻塞状态。


重量级锁


重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。


重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。


简而言之,重量级锁和我们平常使用的lock区别不大,一旦有一个线程获取到了锁,其他没有获取到锁的线程都只能进入阻塞状态排队。


锁升级状态


所以,锁升级有两条线路:


  1. 偏向锁打开的情况下,当对象被new出来,此时是无锁,当第一个线程来访问并获取锁的时候,加一个偏向锁,如果在第一个线程持有偏向锁期间,有其他的线程也来获取锁,那么此时升级为轻量级锁,如果竞争比较激烈,比如自旋的线程尝试多次仍旧获取不到锁,就有可能升级为重量级锁。也就是无锁→偏向锁→轻量级锁→重量级锁


  1. 偏向锁关闭的情况下,当对象被new出来,此时是无锁,当第一个线程来访问,直接就加轻量级锁,如果竞争比较激烈,比如自旋的线程尝试多次仍旧获取不到锁,就有可能升级为重量级锁。也就是无锁→轻量级锁→重量级锁


总结


以上是我的思考,有不对的地方欢迎大家指出(我觉得肯定有不对的地方,可是现阶段我就是这么理解的,希望大家积极指出),感谢大家帮助我提高。


参考


synchronized与锁升级(示例代码)


synchronized四种锁状态的升级

目录
相关文章
|
4月前
|
存储 缓存 安全
关于Synchronized锁升级,你该了解这些
关于Synchronized锁升级,你该了解这些
90 3
|
28天前
|
存储 Java C++
人人都会的synchronized锁升级,你真正从源码层面理解过吗?
本文结合Jvm源码层面分析synchronized锁升级原理,可以帮助读者从本质上理解锁升级过程,从锁如何存储,存储在哪的疑问出发,一步一步彻底剖析偏向锁,轻量级锁,自旋锁,重量级锁的原理和机制。
人人都会的synchronized锁升级,你真正从源码层面理解过吗?
|
4月前
|
Java 编译器
多线程(锁升级, 锁消除, 锁粗化)
多线程(锁升级, 锁消除, 锁粗化)
42 1
|
4月前
|
存储 安全 Java
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
64 1
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
|
4月前
|
Java 编译器 程序员
synchronized 原理(锁升级、锁消除和锁粗化)
synchronized 原理(锁升级、锁消除和锁粗化)
|
安全 Java
锁升级原理
锁升级是指在多线程环境下,当一个线程持有了低级别的锁(如偏向锁或轻量级锁)时,如果有其他线程也要获取这个锁,那么就需要将锁升级为重量级锁。这样可以保证在并发情况下,多个线程之间的互斥访问。
218 1
|
存储 安全 Java
08.从源码揭秘偏向锁的升级
大家好,我是王有志。上一篇学习了synchronized的用法,今天我们深到synchronized的原理,来学习偏向锁升级到轻量级锁的过程。
155 0
08.从源码揭秘偏向锁的升级
|
存储 Java
一文打通锁升级(偏向锁,轻量级锁,重量级锁)
一文打通锁升级(偏向锁,轻量级锁,重量级锁)
|
存储 Java
synchronized锁升级原理
synchronized锁升级原理
256 1
synchronized锁升级原理
|
缓存 Java 数据库
synchronized锁升级的过程
之前只是了解过一些悲观锁的底层原理,和他具体是如何锁住线程的一些细节,正好今天休息,结合一些文章和自己的实践操作,整理成了一篇关于synchronized锁升级的过程,希望能对大家有所帮助.
179 0
synchronized锁升级的过程