Epoch的作用?如何解决解决大量线程竞争偏向锁的问题?

Epoch的作用?如何解决解决大量线程竞争偏向锁的问题?

Epoch的作用?如何解决解决大量线程解决偏向锁的问题?

epoch是一个时间戳,用来表明偏向的合法性https://juejin.im/post/5c17964df265da6157056588
每个class对象会有一个对应的epoch字段,每个处于偏向锁状态对象的Mark Word中也有该字段,其初始值为创建该对象时class中的epoch的值。每次发生批量重偏向时,就将该值(类中)+1,同时遍历JVM中所有线程的栈,找到该class所有正处于加锁状态的偏向锁,将其epoch字段改为新值。下次获得锁时,发现当前对象的epoch值和class的epoch不相等,那就算当前已经偏向了其他线程,也不会执行撤销操作,而是直接通过CAS操作将其Mark Word的Thread Id 改成当前线程Id。https://www.jishuwen.com/d/2Z1b

批量重偏向:发生批量重偏向时,将class中的epoch值+1,同时遍历JVM中所有线程栈, 找到该class所有正处于加锁状态的偏向锁对象,将其对象的epoch字段改为class中epoch的新值。下次获得锁时,发现当前对象的epoch值和class的epoch不相等(说明该对象目前没有线程在执行同步块),所以算当前对象已经偏向了其他线程,也不会执行撤销操作,而是可以直接通过CAS操作将其Mark Word的Thread Id 改成当前线程Idhttps://www.loongzee.com/2019/04/11/JavaSynchronized_1/

epoch在批量重偏向的使用过程
1.首先引入一个概念epoch,其本质是一个时间戳,代表了偏向锁的有效性,epoch存储在可偏向对象的MarkWord中。除了对象中的epoch,对象所属的类class信息中,也会保存一个epoch值。
2.每当遇到一个全局安全点时(这里的意思是说批量重偏向没有完全替代了全局安全点,全局安全点是一直存在的),比如要对class C 进行批量再偏向,则首先对 class C中保存的epoch进行增加操作,得到一个新的epoch_new
3.然后扫描所有持有 class C 实例的线程栈,根据线程栈的信息判断出该线程是否锁定了该对象,仅将epoch_new的值赋给被锁定的对象中,也就是现在偏向锁还在被使用的对象才会被赋值epoch_new。
4.退出安全点后,当有线程需要尝试获取偏向锁时,直接检查 class C 中存储的 epoch 值是否与目标对象中存储的 epoch 值相等, 如果不相等,则说明该对象的偏向锁已经无效了(因为(3)步骤里面已经说了只有偏向锁还在被使用的对象才会有epoch_new,这里不相等的原因是class C里面的epoch值是epoch_new,而当前对象的epoch里面的值还是epoch),此时竞争线程可以尝试对此对象重新进行偏向操作https://www.itqiankun.com/article/bias-lock-epoch-effect

自我总结:
应用于批量重定向,类中有epoch(时间戳),属于这个类的所有对象中的mark word也有这个epoch(初始值和类的一样),批量偏向的时候,类种的epoch+1,找到所有属于这个类的正在加锁的偏向锁对象(偏向锁就是对象),偏向锁对象中的mark word中的epoch值直接赋值为class中的epoch,不相等说明偏向了其他线程,这时候不会撤销,而是可以直接通过CAS操作将其Mark Word的Thread Id 改成当前线程Id
相当于略过了锁的撤销的过程,而是直接到了箭头所指的,已偏向状态的id不相等的地方省略了多个线程撤销带来的性能问题。

批量重偏向(bulk rebias)机制是为了解决:一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,这样会导致大量的偏向锁撤销操作。

批量撤销(bulk revoke)机制是为了解决:在明显多线程竞争剧烈的场景下使用偏向锁是不合适的。
https://www.jishuwen.com/d/2Z1b


> 批量撤销意味着同时禁用一些类的所有实例的偏向锁。这些类的对象通常是存在竞争的。[https://ccqy66.github.io/2018/03/07/java%E9%94%81%E5%81%8F%E5%90%91%E9%94%81/](https://ccqy66.github.io/2018/03/07/java%E9%94%81%E5%81%8F%E5%90%91%E9%94%81/)

为什么有批量重偏向
当只有一个线程反复进入同步块时,偏向锁带来的性能开销基本可以忽略,但是当有其他线程尝试获得锁时,就需要等到safe point时将偏向锁撤销为无锁状态或升级为轻量级/重量级锁。这个过程是要消耗一定的成本的,所以如果说运行时的场景本身存在多线程竞争的,那偏向锁的存在不仅不能提高性能,而且会导致性能下降。因此,JVM中增加了一种批量重偏向/撤销的机制。https://www.itqiankun.com/article/bias-lock-epoch-effect

以class为单位,为每个class维护一个偏向锁撤销计数器,每一次该class的对象发生偏向撤销操作时,该计数器+1,当这个值达到重偏向阈值(默认20)时,JVM就认为该class的偏向锁有问题,因此会进行批量重偏向。

当达到重偏向阈值后,假设该class计数器继续增长,当其达到批量撤销的阈值后(默认40),JVM就认为该class的使用场景存在多线程竞争,会标记该class为不可偏向,之后,对于该class的锁,直接走轻量级锁的逻辑。https://www.jishuwen.com/d/2Z1b

image.png

总结:
批量偏向、撤销:解决大量线程竞争偏向锁的问题,两者都让他们略过了撤销的这段逻辑,批量偏向直接到了cas那里,批量撤销直接到了轻量级锁的逻辑。

偏向锁撤销计数器阈值20(每个类中存储,发生一次撤销就加一),超过20批量重偏向,假如之后还进行增长了(这个技术器是针对于class对应的许多对象偏向锁),到了40就批量撤销。

批量重偏向用的是epoch。

参考:
https://www.jishuwen.com/d/2Z1b
https://www.itqiankun.com/article/bias-lock-epoch-effect
https://ccqy66.github.io/2018/03/07/java%E9%94%81%E5%81%8F%E5%90%91%E9%94%81/
https://www.loongzee.com/2019/04/11/JavaSynchronized_1/
https://juejin.im/post/5c17964df265da6157056588

发布了544 篇原创文章 · 获赞 3190 · 访问量 256万+

猜你喜欢

转载自blog.csdn.net/dataiyangu/article/details/104973172