关于Java中的锁机制的粗浅理解


前言

并发编程中会涉及到锁的概念,目前接触到的是两套机制,根据现有理解,简单理解一下,后边如果发现有不妥之处,再来纠正

一、第一套机制:基于Monitor的Synchronized + 锁对象 的隐式锁机制

这一套机制立身之本是Monitor

  1. 每一个对象都有对象头
  2. 每一个对象都有一个monitor与之关联
  3. Synchronized 实现锁的机制,本质上是利用了对象头和monitor
  4. monitor结构详解:
    接下来我们首先看下在monitor在Java虚拟机(HotSpot)中的实现,其实现是基于C++的,由ObjectMonitor实现的,其主要数据结构如下:
  ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

在上面的源码我们可以看到ObjectMonitor中有几个关键属性:

  • _owner:指向持有ObjectMonitor对象的线程
  • _WaitSet:存放处于wait状态的线程队列
  • _EntryList:存放处于等待锁block状态的线程队列
  • _recursions:锁的重入次数
  • _count:用来记录该线程获取锁的次数
    当多个线程同时访问一段同步代码时,首先会进入_EntryList队列中,当某个线程获取到对象的monitor后进入_Owner区域并把monitor中的_owner变量设置为当前线程,同时monitor中的计数器_count加1。即获得对象锁。
    若持有monitor的线程调用wait()方法,将释放当前持有的monitor,_owner变量恢复为null,_count自减1,同时该线程进入_WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。如下图所示:
    在这里插入图片描述
    上边关于ObjectMonitor的结构摘抄自<Moniter的实现原理>,感兴趣可以去看
  1. 我们可以看出其实Monitor提供了两个集合,
    _WaitSet:存放处于wait状态的线程队列
    _EntryList:存放处于等待锁block状态的线程队列
    其中:
    1. _EntryList: 这个集合被 Synchronized 间接调用,例如:
        synchronized (锁对象){
            代码...去酱油
        }

这段代码就是同时出现了 Synchronized + 锁对象, 其实本质上是Synchronized 召唤出了锁对象---->对象头----MarkWord---->Monitor---->_EntryList, 然后就是当第一个线程Threa_1来访问 Synchronized 同步块时,线程Threa_1就被Monitor的_owner标记下来,在Threa_1释放之前,后边再来访问Synchronized 同步块的线程,将会被放入 Monitor的_EntryList中阻塞, 所以说 上边的代码只有一个功能,就是防止多个线程同时进入Synchronized 同步块的功能
2. _WaitSet: 这个集合又是干啥用的呢?,我们看:

        synchronized (锁对象){
            代码...开始买酱油;      //发现商店没有你要的酱油
            锁对象.wait();         //等一下,老板调货
            代码...终于买到酱油了; //酱油调来了,老板会通知你,然后你也买到了
        }

这段代码出现了 锁对象.wait(); 这一句是干啥的呢? 这一句是通过锁对象调用了---->对象头----MarkWord---->Monitor---->_WaitSet, 让当前获得锁的线程先停一下,然后把锁释放出来, 然后自己进入_WaitSet中等待,所以可以看出,其实我们的锁机制还提供了线程的中途等待,就是说,你获得了锁,并不是说你去商店买酱油,一定要买到才能出来,而是如果你发现商店没有,老板说他去调货,你反正也没事,就到商店外的休息区等待老板喊你,等到老板喊你了,你在尝试进来商店,按老板给你说的价格直接买酱油;

  1. 简单总结:
    1. Monitor的_EntryList 被synchronized 调用,用来存储阻塞线程
    2. Monitor的_WaitSet被 锁对象 调用,用来存储主动释放锁,去做等待的线程
  2. synchronized

二、第二套就是基于AQS的 Lock和Condition 的显式锁机制

这套机制是仿照第一套逻辑做的,但是在具体实现上有所不同,从大的方面来说就是AQS提供了两个队列 [同步队列] , [条件队列]
Lock拦截到的线程都进入[同步队列] 阻塞
Condition 导致等待的线程都进入 [条件队列]
condition 唤醒的线程则是 从 [条件队列] 进入[同步队列] 阻塞

总结

上边两套机制是平行的,实现的功能也是类似,但是第二套机制比第一套机制灵活
对于第二套机制的细节,现在还没有理清楚,但为啥还要总结呢?原因就是发现了第二套机制在功能上和第一套机制是一样的,有了这个认识,在学习第二套机制的时候思路就不会凌乱

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值