多线程与高并发—— Synchronized 加锁解锁流程

本文深入探讨了Java Synchronized的优化细节,包括自旋锁在竞争不激烈时的性能优势,轻量级锁的加锁解锁流程,以及偏向锁的概念和重入机制。文章指出,自旋锁在CPU资源允许的情况下能提高性能,而轻量级锁通过Lock Record实现无锁状态的快速加锁。同时,文章详细解析了偏向锁的工作原理,强调其在单线程场景下的性能提升,并讨论了批量重偏向和批量撤销的优化策略。
摘要由CSDN通过智能技术生成

前言

由于 Monitor 基于操作系统调用,上下文切换导致开销大,在竞争不激烈时性能不算很好, 在 jdk6 之后进了系列优化。前文对优化措施进行了简单介绍,下面将一一介绍这些优化的细节,行文思路大致如下:

  1. 从重量级锁的优化开始讲,一是自旋锁,二是尽量避免进入 Monitor ,即使用轻量级锁
  2. 讲解轻量级锁及加锁解锁流程
  3. 轻量级锁在没有竞争时,每次重入仍然需要执行cas操作,为解决这个问题,因而产生了偏向锁
  4. 详细介绍偏向锁

Synchronized 锁的细节

一、自旋锁

自旋锁比较简单,逻辑在上篇也已经进行过阐述,这一篇章我们着重看下它的性能如何?

在竞争度较小的时候,重量级锁的上下文切换导致的开销相对于 CPU 处理任务的时间占比较重,此种情况下,自旋锁的性能有优势,因自旋而导致的 CPU 浪费在可接受范围内;当竞争激烈的时候,继续使用自旋锁则得不偿失,性能上比直接使用重量级锁要差,大量的等待锁的时间被浪费。

根据任务处理时间不同,自旋锁表现也不一,在任务持续时间长的情况下,自旋太久显然是对 CPU 时间片的浪费,且因任务持续时间长,在 10 此默认自旋次数的情况下,易出现自旋结束也无法获取到锁,那么此次空转就是毫无收益的性能浪费。在任务处理时间较短的情况下,显然自旋获得锁的几率要大,因此如果对要执行的任务有很明确的处理时长认知,可以根据情况适当的调整初始自旋次数,JVM 参数为:-XX:PreBlockSpin。

二、轻量级锁

根据观察,多线程中并不总是存在着竞争,使用轻量级锁避免了锁 Monitor 这繁重的数据结构,轻量级锁通常只锁一个字段(锁记录),在 HotSpot 中的实现是在当前线程的栈帧中创建锁记录结构(Lock Record)。

1.轻量级锁加锁流程

  1. 在当前线程的栈帧中创建 Lock Record
  2. 构建一个无锁状态的 Displaced Mark Word
  3. 将 Displaced Mark Word 存储到 Lock Record 中的 _displaced_header 属性
  4. CAS 更新 Displaced Mark Word 指针,注意【3】是将 Lock record 的 header 的值设置成一个 displaced mark word,【4】这一步是将当前对象头的 Mark Word 中的高30 位(全文都是只针对 32 位虚拟机来谈)指向 Lock Record 中的 header。
    4.1 CAS 成功,执行同步代码块
    4.2 CAS 失败,存在两种情况
    3.2.1 判断是否为锁重入(关于轻量级锁的可重入有疑问,见下文)
    3.2.2 锁被其他线程占有,需要竞争锁,进入锁膨胀过程
  5. 加锁成功的话,当前对象的 Mark Word 后两位锁标志位置为 00,余下高位作为指针存储 Lock Record 的地址

轻量级锁加锁源码如下:

// traditional lightweight locking
          if (!success) {
            // markOop就是对象头结构, 生成对象头,这个对象头的状态设置为无锁,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值