呵呵 我们知道 jdk1.6 的时候对 synchronized 关键字的处理做了很多优化, 因此关于 synchronized 多了很多的名词, 偏向锁, 轻量级锁, 自旋锁, 重量级锁 等等
09 给对象添加偏向锁的调试
10 偏向锁的退出的调试
11 偏向锁的重入 以及 线程1获取偏向锁并释放线程2获取锁 的调试
12 给对象添加轻量级锁的调试
13 轻量级锁的重入 以及 线程1获取轻量级锁并释放线程2获取锁 的调试
前面 我们也做了一些 偏向锁, 轻量级锁 的相关调试, 那么 我们这里 便来看一下 锁的膨胀
主要的场景有
1. 偏向锁 膨胀成 轻量级锁
2. 轻量级锁 膨胀成 重量级锁
以下代码 截图, 基于 jdk 9
偏向锁 膨胀成 轻量级锁 主要有一些情况吧
1. 线程1 获取对象的偏向锁执行完成, 线程2 获取对象的锁, 线程2 获取到轻量级锁
2. 线程1 获取对象的偏向锁未执行完成 线程2 获取对象的锁, 线程1的偏向锁 升级为轻量级锁
接下来 我们便来调试一下 如上的一些场景
测试用例如下
package com.hx.test04;
/**
* Test27MultiThreadInBiasLock
*
* @author Jerry.X.He <[email protected]>
* @version 1.0
* @date 2020-04-03 15:14
*/
public class Test27MultiThreadInBiasLock implements Cloneable {
// identStr
private String identStr = "xyz";
int f01;
int f02;
int f03;
int f04;
int f05;
// Test25SynchronizeObject
public static void main(String[] args) throws Exception {
Test27MultiThreadInBiasLock lockObj = new Test27MultiThreadInBiasLock();
doClone(lockObj);
synchronized (lockObj) {
}
new Thread() {
@Override
public void run() {
doClone(lockObj);
synchronized (lockObj) {
}
}
}.start();
Test25SynchronizeObject.sleep(2000);
}
// doClone
private static void doClone(Test27MultiThreadInBiasLock obj) {
try {
obj.clone();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在 synchronizer.cpp fast_enter 里面加上断点, 断点信息如下
现在是 main 线程获取了 lockObj 的偏向锁, 并释放了, 然后 线程0 来获取 lockObj 的锁
发现 lockObj 偏向于 线程0 然后需要处理 撤销偏向锁的相关业务, 以及锁升级
一下为当前场景下面 取消 lockObj 的核心代码
从偏向线程的 线程栈 中获取 BasicObjectLock, 查找 BasicObjectLock.obj 为 lockObj 的 BasicObjectLock 信息
main线程 偏向锁退出的时候 清理了 BasicObjectLock.obj, 因此 这里找不到 BasicObjectLock(表示偏向锁已经退出)
然后 这里之后是将 对象头更新为了 无锁状态(不支持偏向)
这里返回的是 偏向锁已经撤销
回到 synchronizer.cpp fast_enter, cond 为 已撤销, 那么 接着走 后面的 slow_enter 的流程
一下为 synchronizer.cpp slow_enter 的相关代码
这里发现 lockObj 无锁状态(不支持偏向), 线程0 尝试 添加轻量级锁, 添加轻量级锁 成功
线程0 获取到 lockObj 的轻量级锁
我们调整一下用例
package com.hx.test04;
/**
* Test27MultiThreadInBiasLock
*
* @author Jerry.X.He <[email protected]>
* @version 1.0
* @date 2020-04-03 15:14
*/
public class Test27MultiThreadInBiasLock implements Cloneable {
// identStr
private String identStr = "xyz";
int f01;
int f02;
int f03;
int f04;
int f05;
// Test25SynchronizeObject
public static void main(String[] args) throws Exception {
Test27MultiThreadInBiasLock lockObj = new Test27MultiThreadInBiasLock();
doClone(lockObj);
synchronized (lockObj) {
new Thread() {
@Override
public void run() {
doClone(lockObj);
synchronized (lockObj) {
}
}
}.start();
Test25SynchronizeObject.sleep(2000);
}
}
// doClone
private static void doClone(Test27MultiThreadInBiasLock obj) {
try {
obj.clone();
} catch (Exception e) {
e.printStackTrace();
}
}
}
线程0 进入 synchronized.cpp fast_enter
线程0 撤销偏向锁的相关代码
线程0 尝试获取锁的时候, main 线程还未退出 代码同步块, 因此 main 线程中的 main 方法的栈帧中 会存在一个 obj 为 lockObj 的 BasicObjectLock, 这里 找到了这一个记录, highest_lock
然后 因为 main 线程 现在持有锁, 因此 将持有的偏向锁 升级成为了 轻量级锁
然后这里 返回的是 偏向锁已经撤销(升级成了轻量级锁)
线程0 来到 slow_enter 这里
obj() 的偏向锁被 main 线程持有
线程0 来获取锁的时候, 发现 轻量级锁已经被持有 并且不是被当前线程持有
走了 inflate 的流程, 当然 我们这里主要看的是 main 线程持有的 偏向锁 升级为 轻量级锁 的流程
线程1 获取对象的偏向锁未执行完成 线程2 获取对象的锁, 线程1的锁 升级为重量级锁
测试用例 和 线程1 获取对象的偏向锁未执行完成 线程2 获取对象的锁, 线程1的偏向锁 升级为轻量级锁 的这个测试用例相同, 只是我们这里 看的是 线程0 接下来让 锁 膨胀成为重量级锁的过程
线程0 走了后面的 ObjectSynchronizer::inflate 的流程, 将锁升级为了 重量级锁
设置 owner 为 轻量级锁 对应的 BasicObjectLock
然后 在重量级锁的 monitorenter, monitorexit 的处理中, 对于 owner 为某一个线程 或者 owner 为某一个线程的 BasicObjectLock 都进行了兼容处理
所以 确保了 膨胀成了了 重量级锁 之后, 之后的 monitorenter, monitorexit 能够正确的处理
重量级锁的 monitorenter, monitorexit 的 owner 为 THREAD 或者 BasicLockObject 的处理
完
09 给对象添加偏向锁的调试
10 偏向锁的退出的调试
11 偏向锁的重入 以及 线程1获取偏向锁并释放线程2获取锁 的调试
12 给对象添加轻量级锁的调试
13 轻量级锁的重入 以及 线程1获取轻量级锁并释放线程2获取锁 的调试