偏向锁
偏向锁的思想是偏向于让第一个线程获取对象的线程,这个线程之后重新获取该锁不再需要同步操作:
- 当锁对象第一次被线程获得的时候默认进入偏向状态(可能会延迟几秒钟),这时候Mark Word的后两位为101,同时会将线程id记录到Mark Word中.如果CAS操作成功,这个线程以后进入这个锁相关的同步代码块就不需要再进行任何的同步操作
- 当有另一个线程尝试去获取这个锁的对象时,偏向状态就宣告结束,此时JVM会撤销偏向(Revoke Bias) 后恢复到未锁定或者轻量锁状态
一个对象创建时:
- 如果开启了偏向锁(默认开启),那么对象创建后,Mark Word的最后三位值为101,此时thread等其他都为0
- 偏向锁默认时延迟的,不会再程序启动时立刻生效,如果像避免延迟.
- 可以加参数
-XX:BiasedLockingStartupDelay=0
来禁用延迟. - 当一个线程已经计算过hashcode, 也无法进入偏向锁状态,因为hashcode会占用MarkWord字节的31位,没有位置再加偏向锁,而轻量级锁的hashcode存储在lock锁对象中,重量级锁的hashcode存储在monitor中,所以轻量级锁和重量级锁不会出现计算了hashcode后无法加锁的情况
- 添加参数
-XX:-UseBiasedLocking
可以禁止偏向锁的产生
撤销偏向锁的状态:
- 调用对象的 hashCode:偏向锁的对象 MarkWord 中存储的是线程 id,调用 hashCode 导致偏向锁被撤销
- 当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁
- 调用 wait/notify,需要申请 Monitor,进入 WaitSet
批量撤销:如果对象被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID
- 批量重偏向:当撤销偏向锁阈值超过 20 次后,JVM 会觉得是不是偏向错了,于是在给这些对象加锁时重新偏向至加锁线程
- 批量撤销:当撤销偏向锁阈值超过 40 次后,JVM 会觉得自己确实偏向错了,根本就不该偏向,于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的
锁消除
锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除,这是 JVM 即时编译器的优化
享数据的锁进行消除,这是 JVM 即时编译器的优化
锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除(同步消除:JVM 逃逸分析)