详解Java多线程与高并发(二)__锁的底层实现

锁的底层实现:

       由Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步方法 并不是由 monitor enter 和 monitor exit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。

对象内存简图:

详解Java多线程与高并发(二)__锁的底层实现_第1张图片

如上图所示,先介绍几个概念:

对象存在jvm的堆空间中,一个对象包含了对象头、实例变量、填充数据。

对象头:存储了对象的hashCode、锁信息、分代年龄、GC标志。

实例变量:存放类的属性数据信息,包括父类的属性信息。

填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

monitor是监控器,是用来做监控的一个对象

在JVM(Htospot)中,monitor是由ObjectMonitor实现的。

ObjectMonitor中有两个队列_WaitSet和_EntryList,一个标记_Owner

_WaitSet: 用于管理等待队列线程的,即被wait方法阻塞的线程。

_EntryList: 用于管理被阻塞线程(等待持有锁)

_Owner:记录当前持有锁的线程,指向了持有该对象的线程。

注意:每个对象都存在着一个 monitor 与之关联,一一对应的关系,对象与其 monitor 之间的关系有存在多种实现方式,①monitor可以与对象一起创建销毁②当线程试图获取对象锁时自动生成。当一个 monitor 被某个线程持有后,它便处于锁定状态。

加锁机制:

        monitor监控记录多个线程。

        当多线程并发访问同一个同步代码时,首先会进入_EntryList,当一个线程获取锁标记后,对象的对象头中就记录了锁信息,对象头指向了monitor,monitor中_Owner记录此线程,并在monitor中的计数器执行递增计算(+1),代表锁定,其他线程在_EntryList中继续阻塞。

       若执行线程调用wait方法,线程释放锁,monitor中的计数器执行赋值为0,并将_Owner标记赋值为null,代表放弃锁,执行线程进入_WaitSet中阻塞。若执行线程调用notify/notifyAll方法,_WaitSet中的线程被唤醒,进入_EntryList中阻塞,等待获取锁标记。若执行线程的同步代码执行结束,同样会释放锁标记,monitor中的_Owner标记赋值为null,且计数器赋值为0计算。

      注意:当多个线程想要给一个对象加锁时,monitor只和其中持有对象锁的线程一一对应,而其他阻塞的线程被记录在在monitor的_EntryList

 

 线程状态图:

详解Java多线程与高并发(二)__锁的底层实现_第2张图片

 

锁的重入在底层是如何实现的?

在Java中,同步锁是可以重入的。只有同一线程调用同步方法或执行同步代码块,对同一个对象加锁时才可重入。

当线程持有锁时,会在monitor的计数器中执行递增计算,若当前线程调用其他同步代码,且同步代码的锁对象相同时,monitor中的计数器继续递增。每个同步代码执行结束,monitor中的计数器都会递减,直至所有同步代码执行结束,monitor中的计数器为0时,释放锁标记,_Owner标记赋值为null。

 

以上就是关于锁的底层实现,我的一些认识和理解,希望对你有所帮助,欢迎留言、交流、点赞、关注。谢谢!

你可能感兴趣的:(Java多线程与高并发)