写点什么

深入 JVM 内置锁 synchronized 底层

作者:janyxe
  • 2022 年 4 月 19 日
  • 本文字数:1789 字

    阅读完需:约 6 分钟

深入JVM内置锁 synchronized 底层

前言

上一章节带着大家了解了 Java 对象头的组成,本节带着大家了解 synchronized 关键字的底层原理以及锁的升级过程



synchronized 原理详解

synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,可以用来实现对临界资源的同步互斥访问,是可重入

什么是 Monitor

在 Java 虚拟机(HotSpot)中,Monitor 是由 ObjectMonitor 实现的。Synchronized的对象锁,MarkWord 锁标识位为 10,其中指针指向的是 Monitor 对象的起始地址。其主要数据结构如下


ObjectMonitor() {    _header       = NULL;    _count        = 0; // 记录个数    _waiters      = 0,    _recursions   = 0;    _object       = NULL;    _owner        = NULL;    _WaitSet      = NULL; // 处于wait状态的线程,会被加入到_WaitSet    _WaitSetLock  = 0 ;    _Responsible  = NULL ;    _succ         = NULL ;    _cxq          = NULL ;    FreeNext      = NULL ;    _EntryList    = NULL ; // 处于等待锁block状态的线程,会被加入到该列表    _SpinFreq     = 0 ;    _SpinClock    = 0 ;    OwnerIsThread = 0 ;  }
复制代码


ObjectMonitor 中有两个队列,_WaitSet 和 _EntryList,用来保存 ObjectWaiter 对象列表( 每个等待锁的线程都会被封装成 ObjectWaiter 对象 ),_owner 指向持有 ObjectMonitor 对象的线程,当多个线程同时访问一段同步代码时:


  1. 首先会进入 _EntryList 集合,当线程获取到对象的 monitor 后,进入 _Owner 区域并把 monitor 中的 owner 变量设置为当前线程,同时 monitor 中的计数器 count 加 1;

  2. 若线程调用 wait() 方法,将释放当前持有的 monitor,owner 变量恢复为 null,count 自减 1,同时该线程进入 WaitSet 集合中等待被唤醒;

  3. 若当前线程执行完毕,也将释放 monitor(锁)并复位 count 的值,以便其他线程进入获取 monitor(锁);

synchronized 底层原理

synchronized 是基于 JVM 内置锁实现,通过内部对象 Monitor(监视器锁)实现,基于进入与退出 Monitor 对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的 Mutex lock(互斥锁)实现。JVM 内置锁在 1.5 之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销。每个同步对象都有一个自己的 Monitor(监视器锁):



synchronized 锁的升级过程

public class Test04 {    private static Object objectLock = new Object();
public static void main(String[] args) throws InterruptedException { //-XX:BiasedLockingStartupDelay=0 强制开启// System.out.println(">>----------------无锁状态001-------------------<<"); System.out.println(ClassLayout.parseInstance(objectLock).toPrintable()); System.out.println("开启了偏向锁,但是偏向锁没有关联偏向锁线程"); synchronized(objectLock){ // 偏向锁 关联偏向锁线程 System.out.println("开启了偏向锁,偏向是给我们的主线程"); System.out.println(ClassLayout.parseInstance(objectLock).toPrintable()); } // 撤销偏向锁 是另外一个线程与偏向锁线程竞争 new Thread(new Runnable() { @Override public void run() { synchronized (objectLock) { try { System.out.println(ClassLayout.parseInstance(objectLock).toPrintable()); Thread.sleep(5000); System.out.println("子线程:升级为轻量级锁"); } catch (Exception e) {
} } } }, "子线程1").start(); Thread.sleep(1000); sync(); }
public static void sync() throws InterruptedException { System.out.println(" 主线程获取锁 重量级别锁"); //11010000 01000000 synchronized (objectLock) { System.out.println(ClassLayout.parseInstance(objectLock).toPrintable()); } }
}
复制代码



总结

本文主要介绍了 synchronized 底层原理

发布于: 刚刚阅读数: 2
用户头像

janyxe

关注

勤能补拙 2020.05.26 加入

程序员一枚,喜欢 云原生、前端、后端开发领域

评论

发布
暂无评论
深入JVM内置锁 synchronized 底层_JVM_janyxe_InfoQ写作社区