开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情
synchronized 关键字底层原理属于 JVM 层⾯。
同步代码块
public class SynchronizedDemo {
public void method(){
synchronized (this){
System.out.println("Synchronized同步代码块");
}
}
}
使用java -v -p查看字节码文件
从上我们可以看出:
synchronized 同步语句块的实现使⽤的是monitorenter和monitorexit指令,其中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为啥能自动释放资源,也就是底层帮你实现了相关步骤。
同步方法
public class SynchronizedDemo {
public void method(){
synchronized (this){
System.out.println("Synchronized同步代码块");
}
}
public synchronized void method2(){
System.out.println("Synchronized同步方法");
}
}
同意查看字节码命令。
synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是ACC_SYNCHRONIZED标识,该标识指明了该⽅法是⼀个同步⽅法。JVM 通过该ACC_SYNCHRONIZED 访问标志来辨别⼀个⽅法是否声明为同步⽅法,从⽽执⾏相应的同步调⽤。
总结
- synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令
- 其中 monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位 置。
- synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是
ACC_SYNCHRONIZED标识,该标识指明了该⽅法是⼀个同步⽅法。 不过两者的本质都是对对象监视器monitor的获取。