【Java】【JVM】Sychronized底层加锁原理详解

时间:2022-06-01 16:06:31
  • 我们首先先看看JMM模型,话不多说,上图:

【Java】【JVM】Sychronized底层加锁原理详解

  • JMM对应的8大原子操作:
read(读取):从主内存读取数据
load(载入):将主内存读取到的数据写入工作内存
use(使用):从工作内存读取数据来计算
assign(赋值):将计算好的值重新赋值到工作内存中
store(存储):将工作内存数据写入主内存
write(写入):将store过去的变量赋值给主内存中的变量
lock(锁定):将主内存变量加锁,标示为线程独占状态
unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量

【Java】【JVM】Sychronized底层加锁原理详解

  • Sychronized底层对应的JMM模型8大原子操作【lock】和【unlock】
  • 此时即可同时保持
    • 【原子性】:代码成块
    • 【有序性】
    • 【可见性】

感兴趣的同学,可以对代码编译后,看下反编译后的指令,此处我们分析一下:

  • 原理:
    • JVM内置锁通过synchronized使用,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex Lock(互斥锁)实现,它是一个重量级锁,性能较低。

【Java】【JVM】Sychronized底层加锁原理详解

  • 如果此时存在一个object2,那么当object1对应线程结束后,只唤醒object1对应阻塞队列中的线程:

【Java】【JVM】Sychronized底层加锁原理详解

  • Monitor加锁原理:每个同步对象都有一个自己的Monitor(锁监视器)
    • JVM加锁过程:JVM内置锁,有没有办法能够手动控制加锁与解锁?

【Java】【JVM】Sychronized底层加锁原理详解

  • JDK1.6版本之后对synchronized的实现进行了各种优化,如适应性自旋锁、轻量级锁和偏向锁,并默认开启偏向锁
开启偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=

关闭偏向锁:-XX:-UseBiasedLocking

【Java】【JVM】Sychronized底层加锁原理详解

上面详细说了一下重量级锁,如果是轻量级锁,那么就没有对应的【Monitor】监视类,那么轻量级锁是如何进行区分的呢?

  • 来!这里看一下对象结构,不墨迹,撸图:

【Java】【JVM】Sychronized底层加锁原理详解

  • 这里张贴一段jdk源代码(有些深度)
oop.hpp:

class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
markOop.hpp:
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) #正常的对象状态
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) #偏向锁的对象状态
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
  • 这里Mark Word在32位JVM中存储内容为例:

【Java】【JVM】Sychronized底层加锁原理详解

  • 问题又来了,那么JVM内置锁升级优化过程是如何的呢?

【Java】【JVM】Sychronized底层加锁原理详解

  • 偏向锁高效原因:

只需要修改【Object Mark Word】中的【hashcode】标志位为当前线程对应的【Thread ID】;

而如果直接切换到重量级锁,则是一个用户态到内核态的切换。

  • 偏向锁可以被撤销么?

当然可以被撤销,不过要等到当前拥有锁的线程达到安全点,即执行完同步块,才可以撤销。