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

锁的底层实现:

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

对象内存简图:

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

对象存在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中,同步锁是可以重入的。只有同一线程调用同步方法或执行同步代码块,对同一个对象加锁时才可重入。

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

 

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值