重量级锁
轻量级锁
当另一个线程参与到偏向锁竞争时,会先判断 markword 中保存的线程 ID 是否与这个线程 ID 相等,如果不相等,会立即撤销偏向锁,升级为轻量级锁。每个线程在自己的线程栈中生成一个 LockRecord ( LR ),然后每个线程通过 CAS (自旋 )的操作将锁对象头中的 markwork 设置为指向自己的 LR 的指针,哪个线程设置成功,就意味着获得锁。 关于 synchronized 中此时执行的 CAS 操作是通过HotSpot 中 bytecodeInterpreter.cpp 文件 C++ 代码实现的。
如果真的遇到了竞争,我们就会认为轻量级锁已经不适合了,我们就会把轻量级锁升级为重量级锁了。
所以轻量级锁适合用在那种,很少出现多个线程竞争一个锁的情况,也就是说,适合那种多个线程总是错开时间来获取锁的情况。
自旋锁
自旋锁是JDK1.4.2的时候引入的, 默认为关闭状态, 可以使用-XX:+UseSpinning
参数来开启 ,可以用-XX:PreBlockSpin
来控制自旋多少次, 默认是10次。
自适应自旋锁
JDK 6中对自旋锁的优化,引入了自适应的自旋。
自适应意味着自旋的时间不再是固定的了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。
如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而允许自旋等待持续相对更长次数。
另一方面,如果对于某个锁,自旋很少成功获得过锁,那么自旋次数少,以避免浪费处理器资源。
有了自适应自旋,随着程序运行时间的增长及性能监控信息的不断完善,虚拟机对程序锁的状况预测就会越来越精准,虚拟机就会变得越来越“聪明”了。
偏向锁
偏向锁退出时不改变对象头中是偏向锁的标记,也不删除记录的线程ID,什么操作都没有,直接退出。
偏向锁升级:https://blog.csdn.net/haoranhaoshi/article/details/108530134
JDK1.8偏向锁默认不开启。开启方式:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0。关闭方式:-XX:-UseBiasedLocking。
开启偏向锁后,如果执行监视对象的hashCode()方法后导致对象头种不能存储线程ID,就会锁升级。
偏向锁适用于那种,始终只有一个线程在执行一个方法的情况。
可重入锁
synchronized 拥有强制原子性的内部锁机制,是一把可重入锁。因此,在一个线程使用 synchronized 方法时调用该对象另一个 synchronized 方法,即一个线程得到一个对象锁后再次请求该对象锁,是永远可以拿到锁的。在 Java 中线程获得对象锁的操作是以线程为单位的,而不是以调用为单位的。synchronized 锁的对象头的 markwork 中会记录该锁的线程持有者和计数器,当一个线程请求成功后, JVM 会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个 synchronized 方法/块时,计数器会递减,如果计数器为 0 则释放该锁。
互斥锁/排他锁/独占锁
synchronized 是一把互斥锁/排他锁/独占锁,当前线程如果获取到锁,会导致其他线程无法获取到锁。