知识铺: 致力于打造轻知识点,持续更新每次的知识点较少,阅读不累。不占太多时间,不停的来唤醒你记忆深处的知识点。
一、Moniter 场景
1.1 Java 线程同步
由于多线程对资源的访问引发的可见性和原子性带来的安全问题。同步锁,互斥锁,都可以保证同一时刻只有一个线程访问。 这里用到的机制就是都是对资源的监视锁,即Monitor,每个对象都用于自己的监视锁Monitor.
1.2 对象中的锁标识
在Java 对象模型中讲到,每个对象的头部都有三部分组成:Mark Word 、Klass Word、数组长度。 每个对象的锁标识,就在 Mark Word这部分中。 如下图:
1.2.1 正常: biased_lock 0 偏向锁标识 + lock 锁标识 01
1.2.2 偏向锁: biased_lock 1 偏向锁标识 + lock 锁标识 01
1.2.3 轻量级锁:ptr_to_lock_record 锁指针 + lock 锁标识 00
1.2.4 重量级锁:ptr_to_heavyweight_monitor 锁指针 + lock 锁标识 10
1.2.5 进入GC: lock 锁标识 11
二、ObjectMonitor 结构
前面讲到 java.lang.Object 类定义了 wait(),notify(),notifyAll() 方法。 这些都是 native方法,底层是C++来实现的。 这些方法的具体实现,依赖一个叫做ObjectMonitor模式实现,这是JVM内部C++实现的机制。
这里有几个比较重要的字段
2.1 _owner 指向持有ObjectMonitor对象的线程地址。
2.2 _WaitSet 存放调用wait方法,而进入等待状态的线程的队列。
2.3 _EntryList 这里是等待锁block状态的线程的队列。
2.4 _recursions 锁的重入次数。
2.5 _count 线程获取锁的次数。
三、 Monitor 上锁 释放锁
3.1 上锁过程
3.1.1 线程获取资源对象的锁,判断 _owner是否为空。这里操作是通过 CAS操作:比较和交换(Conmpare And Swap),比较新值和旧值的不同,替换,这里会发生ABA问题,接下来文章会详细说明。
3.1.2 如果 _owner为null ,直接把其赋值,指向自己, _owner = self ,同时把重入次数 _recursions = 1, 获取锁成功。
3.1.3 如果 _self == cur 和当前线程一致,说明是重入了, _recursions++ 即可
3.1.4 线程进入对象资源,处理。 同时等待当前线程的释放信号,期间一致持有对象资源的锁。
3.2 释放锁
3.2.1 通过 ObjectMonitor::exit 退出
3.2.2 把线程插入到_EntryList中 _recursions--
3.2.3 再次从 _EntryList 中取出线程
3.2.4 调用unpark退出
具体代码,可以搜索 objectMonitor.cpp ,查看源码。