批量偏向锁撤销
当撤销偏向锁的阈值超过 40 次过后, jvm 会认为不该偏向,于是整个类的所有对象都会变成不可偏向,新创建的对象也不可偏向。 注意: 对于统计批量撤销有个参数:-XX:BiasedLockingDecayTime=25000ms
范围内没有达到 40 次,撤销次数清 0 ,重新记时
public class B1 { public static void main(String[] args) throws Exception { Thread.sleep(5000); List<A> listA = new ArrayList<>(); Thread t1 = new Thread(() -> { for (int i = 0; i <100 ; i++) { A a = new A(); synchronized (a){ listA.add(a); } } try { Thread.sleep(100000000); } catch (InterruptedException e) { e.printStackTrace(); } }); t1.start(); Thread.sleep(3000); Thread t2 = new Thread(() -> { //这里循环了40次。达到了批量撤销的阈值 for (int i = 0; i < 40; i++) { A a =listA.get(i); synchronized (a){ } } try { Thread.sleep(10000000); } catch (InterruptedException e) { e.printStackTrace(); } }); t2.start(); Thread.sleep(3000); System.out.println("打印list中第11个对象的对象头:"); System.out.println((ClassLayout.parseInstance(listA.get(10)).toPrintable())); System.out.println("打印list中第26个对象的对象头:"); System.out.println((ClassLayout.parseInstance(listA.get(25)).toPrintable())); System.out.println("打印list中第90个对象的对象头:"); System.out.println((ClassLayout.parseInstance(listA.get(89)).toPrintable())); Thread t3 = new Thread(() -> { for (int i = 20; i < 40; i++) { A a =listA.get(i); synchronized (a){ if(i==20||i==22){ System.out.println("thread3 第"+ i + "次"); System.out.println((ClassLayout.parseInstance(a).toPrintable())); } } } }); t3.start(); Thread.sleep(10000); System.out.println("重新输出新实例A"); System.out.println((ClassLayout.parseInstance(new A()).toPrintable())); } }
看看输出结果,这部分和上面的批量偏向有些不同。重点关注线程 id
前 20 个对象并没有触发批量重偏向机制,线程 t2 释放锁之后,变成无锁状态;
第 20 - 40 个对象,触发了批量重偏向机制,对象为偏向锁状态,偏向线程 t2, 线程 t2 的 id 为 -1005618939
第 41 个对象之后,也没有触发了批量重偏向机制,对象依然偏向线程 t1 , 线程 t1 的 id 是 -1039208443
t3 线程开始进行锁的竞争,因为已经到到批量撤销的阈值,且对象 listA.get(20) 和 listA.get(22) 已经进行了锁的重偏向了,所以 t3 再次获取锁的时候,不会触发重新偏向为线程 t3.
此刻,触发批量撤销,此对象碰撞变为轻量级锁
最后我们看一下新生成的对象。本来应该是可偏向状态,但是在经历过批量重偏向,和批量撤销之后转换为无锁。
批量重偏向和批量撤销总结
- 批量重偏向和批量撤销针对类的优化,和对象无关。
- 批量锁重新偏向一次过后不能重新偏向。
- 当某个类已经触发批量撤销机制后, JVM 会默认当前类长生了严重的问题,剥夺了该类新生成对象偏向锁的权利。