在多线程并发编程中,synchronized 一直都是重量级的锁,但是随着JDK 版本的更迭,在JDK1.6之后进行了各种优化,让他在一些情况下并不是那么重了。本文将讲解一下锁升级和内部存储的过程。
锁升级
synchronized 的锁升级包括四种状态。
无锁态 —> 偏向锁 —> 轻量级锁 —> 重量级锁
锁只能升级,不能降级,目的就是为了提高获得锁和释放锁的效率。
锁的状态变化
一、无锁态
在程序没有执行的时候,或者说代码块没有执行的时候,synchronized 并不会给代码加锁,这个阶段就是无锁的状态。
二、偏向锁
经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程获得,所以为了降低获得锁的代价,引用了偏向锁。偏向锁是在对象的对象头中将线程的ID添加进去,为了让线程在下次进入和退出同步快时不需要CAS操作加锁和解锁。偏向锁在获取锁时,只是简单测试一下对象头的Mark Word 里面是否存储着当前线程的ID,如果成功了,表示当前线程获得了锁。如果失败了,就通过CAS操作来获取锁。
偏向锁的撤销
偏向锁的撤销采用的竞争出现才释放锁的机制。也就是说,当其他线程竞争同一个锁的时候,持有偏向锁的线程才会释放锁。
可以使用 -XX:BiasedLockingStartupDelay=0 关闭偏向锁的延迟
可以使用 -XX:-UserBiasedLocking=false 关闭偏向锁
三、轻量级锁
1. 轻量级锁加锁
在发生偏向锁的基础上,如果其他线程想要获取锁,通过CAS操作来将Mark Word 中的线程ID改为当前线程,如果失败,则当前线程则会尝试使用自旋操作来获取锁。在发生一定次数的自旋后仍不能获得锁,那么此时就会升级为重量级锁。