synchronized锁升级过程

【一些面试真题】:


阿里P9——0x80的执行过程。

【 重温CAS过程 】:

【硬件】:
Lock指令在执行后面指令的时候锁定一个北桥信号(不采用锁总线的方式)。

【用户态 与 内核态】:

作为操作系统来说,它能够做的操作是不允许普通程序去做的。为了保障操作系统的健壮性,现代的操作系统都会把指令分成级别。
内核态是执行在内核空间的,它能够访问所有的指令。
而用户态的程序只能访问用户能够访问的指令。
synchronized锁升级过程_第1张图片
synchronized锁升级过程_第2张图片
//对于操作系统来说 , 你JVM也仅仅是工作在用户态的。
【 重量级锁 】:
锁这个资源是需要通过操作系统才能够去申请到的。称之为重量级锁的原因是——申请、返回 都需要经过操作系统。
【 早期上重量级锁 】:
JVM要对某个资源加锁了 ,你对资源加锁的时候 , 你得向操作系统申请一把锁——完成从用户态到内核态的调用。
【中断的调用】:
从JVM到OS申请的时候,是要经过一个中断的调用。返回锁的时候,也是要从内核态返回到用户态。
【做的优化】:

某些状态下上锁的话 , 是无需向操作系统申请的,只需要在用户态就可以解决问题。

【JOL对象内存布局】:

JOL = Java Object Layout

一个对象在内存中如何分布这件事情,是和特定的JVM虚拟机实现有关系的,我们今天讲的主要是Hotspot的实现 ,其它的虚拟机有可能不是这种实现。

【四部分构成】:

markword 8、 classpointer 4 、 instance data(看实际的变量类型)、 padding(补齐)。

【markword中】:

其中,最重要的信息就是——锁信息。

【详解锁升级过程】:

synchronized锁升级过程_第3张图片

【锁升级实质】:

通过markword的后几位标志来实现升级的;

【锁升级图】:

锁升级初步

synchronized锁升级过程_第4张图片
//当我们new出一个普通对象的时候,它有可能是两种状态,两种状态的意思是——普通对象/匿名偏向 两种类型的markword是不同的形式,new Object就是一个普通对象。

【new Object】:

我们new Object , 一旦我给这个对象加上了synchronized关键字的时候 , 它会首先升级成偏向锁 ,竞争一旦激烈就会升级成轻量级锁,轻量级锁又叫做自旋锁,竞争再加剧会变成重量级锁——也就是一开始我们提到的,向操作系统老大申请的锁。

【偏向锁未启动】:

synchronized锁升级过程_第5张图片
偏向锁没有启动 , 普通对象也是有可能直接升级成轻量级锁的。

【 如何区分锁的状态 】:

synchronized锁升级过程_第6张图片
00——轻量级锁;
10——重量级锁;
11——GC标记信息;
synchronized锁升级过程_第7张图片
01——必须得靠偏向锁位来区分。
001——无锁状态。
101——偏向锁状态。

【锁的介绍】:

【用户空间锁】:

偏向锁 和 轻量级锁 都是用户空间锁(用户态度锁)—— 我不需要和操作系统打交道。

【重量级锁VS用户空间锁】:

synchronized锁升级过程_第8张图片
//偏向锁——哪个线程先来,我就偏向它就可以了。

【StringBuffer】:

里面的方法是全都加synchronized的。

【形象理解锁机制】:

synchronized锁升级过程_第9张图片

【偏向锁】:

markWord中放的是当前线程指针,所谓的偏向锁是没有必要设计锁竞争机制的,第一个访问这把锁的线程直接把自己的线程ID往上一贴就完了。总而言之是贴上当前线程的标志。

【自旋锁】:

先撤销偏向锁之前贴上的线程标识 ,撤销掉之后进行竞争 ,竞争的方式就是——自旋的竞争 ;
synchronized锁升级过程_第10张图片

LockRecord

每个线程在它的线程栈中生成一个LR(锁记录),将这个LR贴到锁上,锁上的指针指向哪一个线程的LR , 就表示哪个线程持有这把锁。另外的线程只能用CAS机制继续竞争。

【重量级锁】:

//我这把锁必须得向操作系统去申请;MarkWord中实际记录的是一个ObjectMoint——实际就是JVM空间写的一个C++对象 , 而这个C++对象它内部去访问的时候是需要通过操作系统,经过操作系统之后拿到操作系统对应的那一把锁。

【Hotspot源码位置】:

synchronized锁升级过程_第11张图片
InterpreterRuntime::monitorenter方法。
synchronized锁升级过程_第12张图片

【synchronized编译过程】:

当我们在Java文件中写了synchronized{…} 代码块之后,在汇编语句中就会出现:
monitorenter——原 { 位置处,锁开始。
monitorexit—–—原 } 位置出,锁结束。
最后一个monitorexit—–—产生任何异常的话。
synchronized锁升级过程_第13张图片
//最后一个monitorexit是表示——产生了异常,如果发生异常自动释放锁。

【monitorenter】:

synchronized锁升级过程_第14张图片
synchronized锁升级过程_第15张图片
//如果使用了偏向锁 ( 618行的判断 ), 也就是如果偏向锁打开了 ,fast_enter——快速地竞争锁;否则slow_enter。
synchronized锁升级过程_第16张图片
synchronized锁升级过程_第17张图片
synchronized锁升级过程_第18张图片
//首先进入自旋;——升级为自旋锁。

//如果自旋锁不成——锁膨胀。inflate方法——膨胀为重量级锁。

【重量级锁的代码】

synchronizer.cpp 中。

【 锁重入 】:

sychronized是可重入锁。
重入次数必须记录,因为要解锁几次必须得对应。
重入次数记录在哪里呢???
——不同锁的实现是不一样的,偏向锁记录在线程栈里,每增加一次,LR+1。
偏向锁、自旋锁 -> 线程栈 -> LR+1 。
重量级锁 -> ObjectMonitor 字段上。

【概念简介】:

synchronized锁升级过程_第19张图片
方法m( ) 是 synchronized 修饰的 ,方法m加的锁是O , 方法m里面调用了方法 n( ) , 方法n( ) 加的锁也是O , 所以就相当于给锁O上了两次。

【自旋锁什么时候升级成重量级锁?】:

竞争加剧,有线程超过10次自旋,或者自旋线程数超过CPU核数的一半。
synchronized锁升级过程_第20张图片

【 偏向锁启动 】:

【为什么有自旋锁还需要重量级锁?】:

自旋是消耗CPU资源的,如果锁的时间长,或者自旋线程多,CPU会被大量消耗。
//时间都花在线程的自旋上了。
重量级锁里面有各种各样的队列。每个队列的作用都是不同的(竞争 / 执行 / 等待)。
一个线程想申请一把重量级锁,会进入一个队列里。假如10000个线程在等待,这10000个线程会在WaitSet里 ,不需要消耗CPU时间,所以在竞争超级激烈的时候重量级锁会比自旋锁更合适。Linux内核对于进程的调度叫做CFS 。
重量级锁有等待队列,所有拿不到锁的进入等待队列,不需要消耗CPU资源。

【偏向锁是否一定比自旋锁效率高?】:

不一定。
在明确知道会有多线程竞争的情况下,偏向锁肯定会涉及锁撤销,这时候直接关闭偏向锁,这时候直接使用自旋锁。
JVM启动过程,会有很多线程竞争(明确),所以默认情况启动时不打开偏向锁,过一段儿时间再打开。
synchronized锁升级过程_第21张图片
//偏向锁启动的时候,默认有一个时延。

【匿名偏向】:

刚开始偏向锁还没有偏向任何一个线程,所以称之为匿名偏向。

【 偏向锁直接升级成重量级锁 】:

调用了wait( ) 方法,直接进入重量级锁状态。

【所有锁升级路线】:

  • new -> 普通对象
  • 普通对象 -> 轻量级锁
  • new -> 匿名偏向
  • 匿名偏向 -> 偏向锁
  • 偏向锁 -> 轻量级锁
  • 偏向锁 -> 重量级锁
  • 轻量级锁 -> 重量级锁
    synchronized锁升级过程_第22张图片

【普通对象到偏向锁】:

关键在于——Epoch。( 批量重偏向与批量撤销 )
synchronized锁升级过程_第23张图片

【例】:

一个Class , 有很多的对象( 1~19 )。
synchronized锁升级过程_第24张图片
//第一个线程来的时候,会把锁全都给加上。( 20个同一个类的对象 )
synchronized锁升级过程_第25张图片
第二个线程来竞争这个锁的时候——它会直接升级成为《自旋锁》。从第21个对象开始,如果线程又给这个对象来上锁,上的会是偏向锁。——这个锁叫做重偏向。因为它原先偏向过第一个线程,批量重偏向。本来这个锁已经升级到了《轻量级锁》 ,JVM观测到你这个Class对应的对象竞争非常的多,这个时候给你又回到偏向锁这种效率比较高的机制上来 ,这个优化是针对于Class来进行的,它并不是针对于对象进行的,前面讲的都是针对于对象来讲的,而这个优化是针对于Class来说的,线程继续加锁 , 当加到第41个的时候,我认为加偏向锁是有问题的,这个时候就会进行批量锁撤销,会把你以前锁定的这些全部撤销,全升级为《自旋锁》。———批量锁的重偏向、批量锁的撤销。

[ 复习批量重偏向 、批量锁撤销 概念 ]:

synchronized锁升级过程_第26张图片
Class生产的所有对象 ,把你对象的状态全部都换成偏向锁。
如果说你的对象特别多 ,说明竞争非常激烈 ,到第40个的时候,全部给你换成轻量级锁。
//这是一个针对Class的操作。

你可能感兴趣的:(Java多线程,jvm,java,面试)