java多线程基础之Synchronized底层实现

  • 初认synchronized使用规则
    • 一般来说,使用规则一般分为两类,即修饰代码块和修饰方法

    • 修饰代码块:

      synchronized(this|object) {
          // TODO
      }
      synchronized(.class) {
          //TODO
      }
      
    • 修饰方法:

      • 修饰非静态方法
      • 修饰静态方法
  • 从使用规则可以看出,synchronized(this|object),括号内绑定的是对象,那么synchronized的实现原理应该和对象有关系的。
    • 那么,在JVM中是如何存储一个对象的,了解一下对象内存布局。
      • 一个对象内存布局分为:对象头,实例数据,数据填充。
      • 其他的先不管,直接看向对象头,JVM解释如下:
      • 对象头分为两个部分:其一(对象标记),,即Hash码、GC分代年龄、锁状态标记等。其二(元数据),即包含类型指针,即对象指向它元数据的指针,JVM通过该指针确定这个对象是哪个类的实例。
    • 对象头中保存有锁标记的部分,称为对象标记,也称为Mark Word。
  • 初识Mark Word概念
    • 由上文可,mark word就是对象标记,在JVM底层实现如下:

      class oopDesc {
          friend class VMStructs;
       private:
           volatile markOop _mark; //对象标记
          union _metadata {
              Klass* _klass;
              narrowKlass _compressed_klass;
          } _metedate
      }
      
    • 打开markOop类,部分结构如下:

      • 枚举类,字面意思上可以理解,包含分代年龄、锁标记、偏向锁标记、哈希码等。
      • 后面的等号赋值,表示在内存中占用几个比特bit。

      在这里插入图片描述

  • 从markword可以看出,synchronized实现跟对象有关,即对象锁(对象监视器)。
  • 那么在markOop类中的枚举,我们需要关注几个字段:lock_bits(锁)、biased_lock_bits(偏向锁)
    • 在JVM中锁分类:无锁、偏向锁、轻量级锁、重量级锁
    • biased_lock_bits两个比特:0,表示未持有偏向锁。1,表示持有偏向锁。
    • lock_bits一个比特:00(无锁/偏向锁)、01(轻量级锁)、10(重量级锁)、11(GC标记)
      • 当biased_lock_bits为1时,lock_bits为00的时候,表示偏向锁。
  • 其实,在JDK1.6之前,synchronized的实现都是重量级锁,在1.6之后进行了优化,这些锁优化概念,包括偏向锁,轻量级锁,自旋,锁膨胀等,synchronized底层原理与这些知识点分不开。

  • 那么什么叫偏向锁?
    • 偏向锁是JDK1.6之后引入的一种锁优化,目的就是消除数据在无竞争情况下的同步原语。如果说轻量锁是在无竞争环境情况下使用CAS操作去消除同步使用的互斥量,那么偏向锁就是在无竞争环境情况下把整个同步都消除掉,连CAS操作都不要做。
    • 其中,偏向锁含义就是这个锁会偏向第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,那么持有该偏向锁的线程将永远不需要进行同步。比如,线程A买了一张游乐园的票,在游客数目较小且不产生竞争的情况下,线程A可以自由进出游乐园,而不必去竞争一张门票。
  • 偏向锁是如何获取的?
    • 获取目标对象的mark word,synchronized(object){},即获取object对象的对象头信息。

    • 假设JVM开启偏向锁模式,即启用参数-XX:+UseBiasedLocking,那么对象(object)第一次被线程获取的时候,JVM会把其对象头中的标志biased_lock_bits为1时,lock_bits为00,表示开启偏向模式,同时使用CAS操作把获取到这个锁的线程ID记录到对象头(mark word)中,如果CAS操作成功,持有偏向锁的线程有每次进入这个锁相关的同步块时,JVM都不会进行任何同步操作。

    • 然而,当有另外一个线程去尝试获取这个锁时,产生竞争,所有偏性模式结束。之后,根据锁对象当前是都处于被锁定状态进行撤销偏向。

      在这里插入图片描述

  • 什么叫轻量级锁?

    • 如果完全没有锁竞争,那么申请重量级锁是无意义的。那么轻量级锁的目标是,减少无实际竞争情况下,使用重量级锁产生的性能消耗,包括系统的调用引起的内核态与用户态切换、线程阻塞造成的线程间切换等。
  • 轻量级锁获取和释放

    • 轻量级锁的获取:
      • JVM首先会在当前线程的栈帧中创建一个帧记录,称为锁记录空间(Lock Record),用于存储锁对象目前的Mark word的拷贝(Displaced Mark Word),然后,JVM会尝试使用CAS操作将对象的Mark Word更新为指向Lock Record的指针。
      • 如果成功,则当前线程就拥有对象锁,并且对象的Mark Word中的标记lock_bits = 01,表示此对象处于轻量级锁定状态。
      • 如果失败,JVM会首先检查对象的Mark word是否指向当前线程的栈帧。如果指向,说明当前线程已经拥有了这个对象的锁,那么就可以直接进入同步代码块。如果没有指向,则说明这个锁对象已经被其他线程抢占。
      • 如果两条线程以上在争抢同一个锁,那轻量级锁不在有效,锁进行膨胀,膨胀成重量级锁。

    在这里插入图片描述
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值