开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情
上一篇文章,探索 Java 同步锁机制 synchronized 的原理(一) 我们简单学习和了解了 synchronized 的基本使用和 Java 的内置锁机制,今天我们来通过 Java 反编译的技术来看看 synchronized 的实现原理。
synchronized 修饰普通方法
首先我们看看下列代码:
package com.zhongger.threadLearn;
/**
* @author zhongmingyi
* @date 2023/2/15 7:14 下午
*/
public class SynchronizedTest1 {
public synchronized void method1() {
System.out.println("method1 start");
try {
System.out.println("test synchronized method1");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method1 end");
}
public synchronized void method2() {
System.out.println("method2 start");
try {
System.out.println("test synchronized method2");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("method2 end");
}
public static void main(String[] args) {
SynchronizedTest1 test1 = new SynchronizedTest1();
new Thread(new Runnable() {
@Override
public void run() {
test1.method1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
test1.method2();
}
}).start();
}
}
输出的结果如下:
结果分析:
- 线程 1 先获取到 SynchronizedTest1 对象锁的锁之后,执行 method1 方法
- method1 方法会执行 3s,直到执行完成后,释放对象锁
- 线程 2 获取到对象锁之后,再执行 method2 方法
我们使用 javap -v SynchronizedTest1.class 命令反编译这段代码,反编译结如图:
我们可以看到,由 synchronized 修饰的实例方法被添加上了 ACC_SYNCHRONIZED 标识,JVM在解析的时候,根据这个标识实现方法同步。
synchronized 修饰代码块
synchronized 修饰代码块的实例如下:
package com.zhongger.threadLearn;
/**
* @author zhongmingyi
* @date 2023/2/15 7:40 下午
*/
public class SynchronizedTest2 {
public void method() {
synchronized (this) {
System.out.println("test");
}
}
}
我们通过反编译命令来看看这种修饰方式是怎么给对象加锁的。执行命令:
javap -c SynchronizedTest2
输出结果如下:
我们可以看到关于对象锁的指令 monitorenter 和 monitorexit
- monitorenter:每个对象有一个监视器锁 monitor
- 当 monitor 被占用时就会处于锁定状态,线程执行monitorenter 指令时,会尝试获取 monitor的所有权
- 如果 monitor 的进入数为 0 ,则该线程进入 monitor ,然后将进入数设置为 1 ,该线程即为 monitor 的所有者
- 如果线程已经占有该 monitor ,只是重新进入,则进入 monitor 的进入数加 1
- 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
- monitorexit:执行 monitorexit 的线程必须是 objectref 所对应的 monitor 的所有者。
- 指令执行时,monitor 的进入数减1,如果减1后进入数为0,那线程退出 monitor ,不再是这个 monitor 的所有者。其他被这个 monitor 阻塞的线程可以尝试去获取这个 monitor 的所有权。