每一个 Object 都有一个隐含的锁,也称作监视器对象。
在同步的时候是获取对象的 monitor,即获取到对象的锁。
在JDK1.6之前,synchronized是一个重量级锁,性能比较差。从JDK1.6开始,
为了减少获得锁和释放锁带来的性能消耗,synchronized进行了优化,引入了 偏向锁和 轻量级锁的概念。
这几个状态会随着锁竞争的情况逐步升级。
为了提高获得锁和释放锁的效率,锁可以升级但是不能降级。
- 锁标志位
锁标识 lock=00 表示轻量级锁
锁标识 lock=10 表示重量级锁
偏向锁标识 biased_lock=1表示偏向锁
偏向锁标识 biased_lock=0且锁标识=01表示无锁状态-
无锁状态
-
偏向锁(Biased Lock)
大多数情况下,锁不仅不存在多线程竞争 而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。
偏向锁可以通过 -XX:+UseBiasedLocking开启或者关闭
偏向锁的获取 偏向锁的获取过程非常简单,当一个线程访问同步块获取锁时, 会在对象头和栈帧中的锁记录里存储偏向锁的线程ID,表示哪个线程获得了偏向锁
偏向锁的撤销 当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放偏向锁, 撤销偏向锁的过程需要等待一个全局安全点(所有工作线程都停止字节码的执行
- 轻量级锁(Lightweight Lock)
偏向锁撤销以后对象会可能会处于两种状态 一种是不可偏向的无锁状态, 简单来说就是已经获得偏向锁的线程已经退出了同步代码块,那么这个时候会撤销偏向锁,并升级为轻量级锁 一种是不可偏向的已锁状态, 简单来说就是已经获得偏向锁的线程正在执行同步代码块,那么这个时候会升级到轻量级锁并且被原持有锁的线程获得锁
- 重量级锁(Heavyweight Lock)
重量级锁依赖对象内部的monitor锁来实现,而monitor又依赖操作系统的MutexLock(互斥锁)
为什么重量级锁的开销比较大呢?
原因是当系统检查到是重量级锁之后,会把等待想要获取锁的线程阻塞,被阻塞的线程不会消耗CPU, 但是阻塞或者唤醒一个线程,都需要通过操作系统来实现, 也就是相当于从用户态转化到内核态,而转化状态是需要消耗时间的