synchronized(关键字)-锁膨胀| 8月更文挑战

465 阅读3分钟

这是我参与8月更文挑战的第19天

(一)、基础概念

1、Mark Word

java对象头的Mark Word中存储了HashCode、分代年龄、锁状态等信息,来看下Mark Word的结构

2、栈帧

方法执行时,在jvm的栈中会创建一个栈帧用来存储局部变量、操作数栈、动态链接、方法出口等信息。方法从调用到执行完成,就是栈帧在虚拟机栈中入栈到出栈的过程。(所以代码块中的局部变量可以实现入栈创建,出栈销毁)。

线程中的许多方法同时处于执行状态,对执行引擎来说,活动线程中,栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧关联的方法称为当前方法。

二、偏向锁、轻量级锁和重量级锁的使用和锁膨胀

1、偏向锁

加锁成功:将Mark Word中的线程Id标记为当前线程id,就加锁成功了,具体的流程如下:

(1)、先读取对象的Mark Word 判断是否处于可偏向的状态,即检查Mark Word中的 是偏向状态和锁标志位

(2)、如果可偏向的,说明当前线程可加锁,那么就用CAS操作去将线程id写入到Mark Word中,如果获取锁成功,执行同步代码。

(3)、如果CAS操作失败,说明其他线程在竞争,并取到了偏向锁,那么等待全局安全点(GC运行之前所有线程需要在安全点阻塞,这就GC过程中常说的Stop The World),将偏向状态改为0,验证已获取锁的线程是否存活,如果死亡,将锁标志位恢复到无锁状态,重新加锁。如果存活,将锁标志位升级为轻量级锁(01)。

2、轻量级锁

轻量级锁采用CAS自旋锁的方式来完成加锁,相对于重量级锁加锁的代价相对小一些,如果一直获取不到锁状态,自旋占用的资源会超过重量级锁,所以轻量级锁膨胀为重量级锁的条件就是自旋达超过一定次数(默认为10,可以修改PreBlockSpin参数调整)。

加锁流程:

1、执行同步代码块之前,JVM会在线程的栈帧中创建一个存储锁记录的空间(Lock Record),并将Mark Word拷贝复制到锁记录中。

2、然后尝试通过CAS将Mark Word中的锁记录的指针,指向创建的Lock Record。如果成功表示获取锁状态成功。

3、如果失败,则进入自旋获取锁状态。

3、重量级锁

当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cup。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。