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

  • 我们首先先看看JMM模型,话不多说,上图:

 

 

 

 

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

 

 

 

  • Sychronized底层对应的JMM模型8大原子操作【lock】和【unlock】

 

 

  • 此时即可同时保持
    • 【原子性】:代码成块
    • 【有序性】
    • 【可见性】

 

 

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

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

 

 

 

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

 

 

 

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

 

 

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

关闭偏向锁:-XX:-UseBiasedLocking

 

 

 

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

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

 

 

  •  这里张贴一段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中存储内容为例:

 

 

 

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

 

 

 

 

 

 

  • 偏向锁高效原因:

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

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

 

 

  • 偏向锁可以被撤销么?

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

 

posted @ 2020-05-22 00:15  boluo1230  阅读(555)  评论(0编辑  收藏  举报