synchronized关键字的底层原理

170 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情

synchronized 关键字底层原理属于 JVM 层⾯。

同步代码块

image-20210328120105440

public class SynchronizedDemo {
    public void method(){
        synchronized (this){
            System.out.println("Synchronized同步代码块");
        }
    }
}

使用java -v -p查看字节码文件

image-20210328120229748

从上我们可以看出:

synchronized 同步语句块的实现使⽤的是monitorentermonitorexit指令,其中monitorenter指令指向同步代码块的开始位置, monitorexit指令则指明同步代码块的结束位置。

当执⾏monitorenter指令时,线程试图获取锁也就是获取对象监视器 monitor的持有权。

在 Java 虚拟机(HotSpot)中,Monitor 是基于 C++实现的,由ObjectMonitor实现的。每个对 象中都内置了⼀个 ObjectMonitor 对象。

另外, wait/notify 等⽅法也依赖于 monitor 对象,这就是为什么只有在同步的块或者⽅法 中才能调⽤ wait/notify 等⽅法,否则会抛出 java.lang.IllegalMonitorStateException的异常的 原因。

在执⾏ monitorenter 时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将锁计数器设为 1 也就是加 1。

在执⾏ monitorexit 指令后,将锁计数器设为 0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外⼀个线程释放为⽌。

至于为啥会有两个monitorexit 指令。是因为防止同步代码块存在异常,无法正常释放资源。相当于try-catch-finally在finally中释放资源。这也就解释了为啥synchronized为啥能自动释放资源,也就是底层帮你实现了相关步骤。

同步方法

image-20210328120923832

public class SynchronizedDemo {
    public void method(){
        synchronized (this){
            System.out.println("Synchronized同步代码块");
        }
    }

    public synchronized void method2(){
        System.out.println("Synchronized同步方法");
    }
}

同意查看字节码命令。

image-20210328121356254

synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是ACC_SYNCHRONIZED标识,该标识指明了该⽅法是⼀个同步⽅法。JVM 通过该ACC_SYNCHRONIZED 访问标志来辨别⼀个⽅法是否声明为同步⽅法,从⽽执⾏相应的同步调⽤。

总结

  1. synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令
  2. 其中 monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位 置。
  3. synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。 不过两者的本质都是对对象监视器monitor的获取。