Java 加锁与解锁

今天看极客时间的时候看到了 java 锁的一篇文章,其中关于加锁与解锁的几个点不是特别理解,晚上专门差了一下资料,算是弄明白了。java

疑问

为何解锁的时候,cas 释放失败就证实有其余线程来获取过锁呢?安全

原理

每个java对象都拥有一个对象头,对象头分为2个部分:Mark Word(标记字段) 与 Class Pointer(类型指针)。jvm

  • Mark Word用于存储对象自身的运行时数据,自身运行时的数据:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等,它是实现轻量级锁和偏向锁的关键。
  • Class Pointer是对象指向它的类元数据的指针,虚拟机经过这个指针来肯定这个对象是哪一个类的实例。

32位虚拟机的Mark Word以下:
Mark Word可能存储4种数据post

分析

先说一下锁的种类:偏向锁,轻量级锁,重量级锁。spa


偏向锁

获取流程:

  1. 确认对象是可偏向状态(是否偏向:1,锁标致:01);
  2. 可偏向,查询 Mark Word 中 线程ID是否为当前线程ID,是则进行5,不是则3;
  3. 未指向当前线程,则 CAS 尝试获取锁,获取成功,将 Mark Word 中线程ID更换,执行5,不然执行4;
  4. 若是获取失败,则说明有线程竞争,那么在 safe-point 安全点时,获取锁的线程挂起,升级为轻量级锁,而后被阻塞在安全点的线程继续执行后面的代码;
  5. 执行代码。

释放流程:

不主动释放,只有当出现竞争,进入到安全点时才会触发释放逻辑,这也是3为何会获取成功的缘由。
结果是要么释放被其余线程获取,要么升级轻量级锁。线程


轻量级锁

获取流程:

  1. 线程进入同步代码块时,若是是偏向锁的状态;线程在本身的栈帧空间内创建一个 Mark Record 的空间,用于存储 Mark Word 的备份,官方称为 Displaced Marl Word,这是线程与对象头的状态是这样的:
  2. 拷贝 Mark Word 到 Lock Record 中;
  3. CAS 尝试将对象头的 Mark Word 指向 Lock Record,并将 Lock Record 中的 owner 执行 mark word,若是成功执行4,不然5;
  4. 若是更新成功,就证实获得锁,并将锁标致位更新为00,表示对象处于轻量级锁,此时线程与对象头的状态:
  5. 若是更新失败,jvm 先判断 mark word 是否指向当前栈帧,若是是表明线程已经获取锁,进入重入状态;若是不是证实已经被其余线程获取,自旋等待必定时间,若是仍未获取,升级为重量级锁(将 mark work 从指向线程指针改成指向互斥量)。

释放流程

  1. 经过 CAS 将线程中复制的 Displaced Mark Work 替换会原先的 Mark Word;
  2. 替换失败,说明有其余线程尝试过获取该锁(此时锁已膨胀),那就要在释放锁的同时,唤醒被挂起的线程。

这里说一下轻量级锁释放失败是就证实锁升级的原理,由于以前 mark word 指向的是本线程的指针,这个是 cas 指望的值,可是被其余线程更改成了指向互斥量的对象了,cas 就失败,就证实升级为了重量级锁。指针

参考文档

https://www.itqiankun.com/art...
https://juejin.im/post/5b4eec...code

相关文章
相关标签/搜索