synchronized关键字总结
在并发编程中,synchronized 关键字非常常见,在 JDK 源码中也有很多,典型基础的类有 StringBuffer,我们知道 StringBuilder 是非线程安全的,StringBuffer 是线程安全的,就是因为 StringBuffer 源码的一些方法上使用了synchronized 关键字来保证同步。
synchronized关键字原理
synchronized 代码块同步
1 | public class SynchronizedTest { |
上面SynchronizedTest
类的一段代码块用 synchronized 修饰,我们使用 javac SynchronizedTest.java
命令将其进行编译,然后使用javap -c SynchronizedTest.class
反编译其 class 文件观察输出:
1 | Compiled from "SynchronizedTest.java" |
看到有monitorenter 、monitorexit两个指令,synchronized 的原理就是使用对象监视器来实现对方法和代码块的同步的:在进入同步方法前加一个 monitorenter 指令,在退出、异常时加入 monitorexit指令,其本质是对象监视器(monitor)的获取,这个获取具有排他性,所以同一时刻只能有同一个线程获到 monitor,获取到 monitor 的执行代码块、执行完后退出释放 monitor,未获得 monitor 的线程会再次去尝试获得 monitor,最终实现同步的目的。
synchronized 代码块实现细节:
对于 monitorenter,如果 monitor 的进入数为0,则该线程获取锁,并设置为1,再次重入时对其+1,解锁-1,最终释放后等于0。如果 monitor 不为0(大于0),则表示该锁被其他线程占用,则等待 monitor等于0时才获取锁。monitorexit 与之过程相反。
synchronized方法同步
方法同步和代码块同步的实现细节不太一样,上述例子代码块同步是使用 monitorenter 和 monitorexit 指令来实现,而方法同步是用另一种方式来实现的,JVM 根据 ACC_SYNCHRONIZED 标识符来实现方法的同步,当方法调用时,调用指令会检查方法的 ACC_SYNCHRONIZED 标识符是否被设置,如果设置了,线程将获取对象监视器 monitor,获取成功后去执行代码块,执行完后再释放 monitor,方法执行期间,其他线程都无法再获得同一个monitor 对象,实现了方法的同步。
synchronized 实现同步的基础
任何对象都可以作为锁,也就是上述的 monitor,具体表现为:
对于普通同步方法,锁的是当前实例对象;
对于静态同步方法,锁的是 Class 对象;
对于同步代码块,锁的是小括号里的对象;
当一个线程访问同步方法、同步代码块时,必须要先获得锁,退出或抛出异常时必须释放锁。
synchronized 关键字优化
synchronized 一直被称为重量级锁,其实在jdk1.6对其做了优化、引入了轻量锁和偏量锁。
关于轻量锁和偏量锁更形象的描述请参照这篇短文 https://www.jianshu.com/p/afa5296a4832
随着 synchronize 关键字不断优化,jdk8的 ConcurrentHashMap 抛弃了 ReentrantLock 而使用 synchronize。
- Title: synchronized关键字总结
- Author: 薛定谔的汪
- Created at : 2018-07-31 18:01:54
- Updated at : 2023-11-17 19:37:37
- Link: https://www.zhengyk.cn/2018/07/31/java/synchronized/
- License: This work is licensed under CC BY-NC-SA 4.0.