在理解锁实现原理之前先了解一下java的对象头和Monitor,在JVM中,对象是分成三部分存在的:对象头、实例数据、对其填充
实例数据和对其填充与synchronized无关。实例数据存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐;对其填充不是必须部分,由于虚拟机要求对象起始地址必须的8字节的整数倍,对齐填充仅仅是为了使字节对齐。
对象头是我们需要关注的重点,是synchronized 实现锁的基础,因为synchronized申请锁、上锁、释放锁都与对象头有关。对象头主要结构是由 Mark Word 和Class Metadata Address 组成,其中Mark Word 存储对象的hashCode、锁信息或分代年龄或GC标志等信息,Class Metadata Address 是类型指针指向对象的类元数据,JVM通过该指针确定该对象是哪个类的实例
锁分为四个状态:无锁状态、偏向锁、轻量级锁、重量级锁,其中无锁就是一种状态了。
锁的类型和状态都记录在Mark Word中,在申请锁、锁升级过程中JVM都需要读取对象头Mark Word的数据。
每一个锁都对应一个monitor对象,在HotSpot虚拟机中是由ObjectMonitor实现的,每个对象都存在着一个monitor与之关联,对象与其monitor之间的关系存在多种实现方式。如 monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个monitor被某个线程持有后,他便处于锁定状态。
ObjectMonitor 中有两个队列 WaitSet和EntryList用来保存ObjectWaiter对象列表(每个等待锁的线程会被封装成ObjectWaiter对象),owner指向持有ObjectMonitor对象的线程,当多个线程线程同时访问一段同步代码时,首先会进入 EntryList集合里,当线程获取到对象的monitor后进入Owner区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用wait()方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor()并复位变量的值,以便其他线程进入获取monitor(锁)。
monitor对象存在于每个java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么java中任意对象可以作为锁的原因,同时也是notify/notiftyAll/wait等方法存在于顶级对象Object中的原因。
synchronized锁的底层实现
最新推荐文章于 2024-07-19 15:13:42 发布