Synchronized原理

一、Javap反汇编

1.1 反汇编

通过javap反汇编学习synchronized的原理

我们编写一个简单的synchronized代码,如下:

public class Demo05_monitor {

    private static Object obj = new Object();

    public static void main(String[] args) {
        synchronized (obj) {
            System.out.println("1");
        }
    }

    public synchronized void test() {
        System.out.println("a");
    }
}

我们要看synchronized的原理,但是synchronized是一个关键字,看不到源码。

我们可以将class文件进行反汇编。

JDK自带的一个工具: javap ,对字节码进行反汇编,查看字节码指令。

在DOS命令行输入:

javap -p -v -c C:\Users\13666\IdeaProjects\HeiMa\Synchronized\target\classes\com\itheima\demo04_synchronized_monitor\Demo05.class

Synchronized原理

Synchronized原理

反汇编后的效果如下:

Synchronized原理

 Synchronized原理

1.2 monitorenter

首先我们来看一下JVM规范中对于monitorenter的描述:Oracle官方解释

Synchronized原理

Synchronized原理

每一个对象都会和一个监视器monitor关联。监视器被占用时会被锁住,其他线程无法来获取该monitor。

当JVM执行某个线程的某个方法内部的monitorenter时,它会尝试去获取当前对象对应的monitor的所有权。

其过程如下:

1. 若monior的进入数为0,线程可以进入monitor,并将monitor的进入数置为1。当前线程成为monitor的owner(所有者)

2. 若线程已拥有monitor的所有权,允许它重入monitor,则进入monitor的进入数加1

3. 若其他线程已经占有monitor的所有权,那么当前尝试获取monitor的所有权的线程会被阻塞,直到monitor的进入数变为0,才能重新尝试获取monitor的所有权。

monitorenter小结:

synchronized的锁对象会关联一个monitor,这个monitor不是我们主动创建的,是JVM的线程执行到这个同步代码块,发现锁对象没有monitor就会创建monitor,

monitor内部有两个重要的成员变量owner:拥有这把锁的线程,recursions会记录线程拥有锁的次数,当一个线程拥有monitor后其他线程只能等待。

1.3 monitorexit

首先我们来看一下JVM规范中对于monitorexit的描述:Oracle-monitorexit

Synchronized原理

1. 能执行monitorexit指令的线程一定是拥有当前对象的monitor的所有权的线程。

2. 执行monitorexit时会将monitor的进入数减1。当monitor的进入数减为0时,当前线程退出monitor,不再拥有monitor的所有权,

此时其他被这个monitor阻塞的线程可以尝试去获取这个monitor的所有权 

Synchronized原理

总结:

monitorexit释放锁。

monitorexit插入在方法结束处和异常处,JVM保证每个monitorenter必须有对应的monitorexit。

面试题synchroznied出现异常会释放锁吗?

会释放锁

1.4 同步方法

Oracle-同步方法

Synchronized原理

Synchronized原理  

可以看到同步方法在反汇编后,会增加ACC_SYNCHRONIZED 修饰。会隐式调用monitorenter和monitorexit。

在执行同步方法前会调用monitorenter,在执行完同步方法后会调用monitorexit。

总结:

通过javap反汇编我们看到synchronized使用编程了monitorentor和monitorexit两个指令。

每个锁对象都会关联一个monitor(监视器,它才是真正的锁对象),它内部有两个重要的成员变量owner会保存获得锁的线程,

recursions会保存线程获得锁的次数,当执行到monitorexit时,recursions会-1,当计数器减到0时这个线程就会释放锁

视频教程参考博客

上一篇:synchronized其他知识


下一篇:getc和fgetc的区别