从网上看了不少批量重偏向与批量撤销的介绍,但都只知其一;不知其二,本着钻研的精神,使用数据进行分析批量重偏向与批量撤销的工做机制。java
首先,要先知道偏向锁的偏向锁机制,着重看下撤销机制。
而后,要知道【批量重偏向与批量撤销】的研究对象是Class,即锁对象对应的Class,而非锁对象自己。jvm
// 研究的是对象.class synchronized(对象) { } // 并非说的这种,这种不考虑 synchronized(对象.class) { }
如对机制不了解,能够看下这个博客 http://www.javashuo.com/article/p-kqdfoqoq-gt.html
另外几个jvm参数:spa
-XX:BiasedLockingBulkRebiasThreshold = 20 // 默认偏向锁批量重偏向阈值 -XX:BiasedLockingBulkRevokeThreshold = 40 // 默认偏向锁批量撤销阈值 -XX:+UseBiasedLocking // 使用偏向锁,jdk6以后默认开启 -XX:BiasedLockingStartupDelay = 0 // 延迟偏向时间, 默认不为0,意思为jvm启动多少ms之后开启偏向锁机制(此处设为0,不延迟)
在这里,我把博客中的图拷过来,方便你们思考。.net
咱们主要看下图中的 “是否开启重偏向?” 这个条件分支,来看下什么是重偏向?线程
重偏向字面理解就是从新偏向,那什么状况下会从新偏向呢
咱们首先要知道的是,锁对象Class刚开始的状况下,是没有开启重偏向的,意思就是 “是否开启重偏向?” 这个分支刚开始的时候是一直走 “否” 的,即会一直撤销偏向锁 ,当达到BiasedLockingBulkRebiasThreshold(20)次数的时候,容许重偏向。code
下面咱们看下代码对象
public class TestBiasedLocking { public static void main(String[] args) throws DecoderException, InterruptedException { // 首先咱们建立一个list,来存放锁对象 List<TestBiasedLocking> list = new LinkedList<>(); // 线程1 new Thread(() -> { for (int i = 0; i < 50; i++) { TestBiasedLocking testBiasedLocking = new TestBiasedLocking(); list.add(testBiasedLocking); // 新建锁对象 synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加锁-线程1"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); // 打印对象头信息 } } }, "线程1").start(); // 让线程1跑一下子 Thread.sleep(2000); // 线程2 new Thread(() -> { for (int i = 0; i < 30; i++) { TestBiasedLocking testBiasedLocking = list.get(i); synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加锁-线程2"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); // 打印对象头信息 } } }, "线程2").start(); LockSupport.park(); } }
先解释下是什么意思,这是输出了三次对象信息,第一行就是对象头信息(分为4组,每组8位,查看方式为,组(左-右),位(右-左)),红框内的最后三位就是最低的三位,即 偏向锁(1bit)+锁标志(2bit),其余研究下就明白了;
1)【线程1】1-50次加锁都是101,即偏向锁状态;这个没什么问题,线程1首次加的锁,而且没有别的线程竞争,因此对象头是偏向锁状态,对应的Thread Id为线程1. 2)【线程2】1-19加的锁都是轻量级锁,即前19次进行了偏向锁撤销,第20次执行了重偏向,线程id指向线程2;
举个不太形象的例子。好比你结婚了,你就属于你老公的了,可是隔壁老王看上你了(线程2加锁),这个时候你是不能和他在一块儿的(不可重偏向),除非离婚(锁消除,升级为轻量级锁,不可再结婚,偏向锁不可逆);有一天,zf统计发现某个地区离婚太频繁了,直接给他们一次换老公的机会(可重偏向),前提是这个机会只给老公不在家的(正在使用的锁对象已经属于正在使用的线程);
下面咱们把例子带入那个数据中,当第19我的离婚后,zf直接给要离婚的人一次换人的机会,因此第20我的就直接把结婚证上的人名换成老王了(Tread Id直接指向线程2)
后来zf发现,换过老公的这批人,离婚仍是那么频繁(重偏向过的锁对象频繁 撤销偏向锁),搞我呢?都玩完!这个地区都别给我结婚了之后(该类下的锁对象都不支持偏向锁了,正在运行的也撤销)。这就是批量撤销的含义,咱们代码来演示下blog
public class TestBiasedLocking { public static void main(String[] args) throws DecoderException, InterruptedException { // 首先咱们建立一个list,来存放锁对象 List<TestBiasedLocking> list = new LinkedList<>(); // 线程1 new Thread(() -> { for (int i = 0; i < 50; i++) { TestBiasedLocking testBiasedLocking = new TestBiasedLocking(); list.add(testBiasedLocking); // 新建锁对象 synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加锁-线程1"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); } } LockSupport.park(); }, "线程1").start(); // 让线程1跑一下子 Thread.sleep(2000); // 线程2 new Thread(() -> { for (int i = 0; i < 40; i++) { TestBiasedLocking testBiasedLocking = list.get(i); synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加锁-线程2"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); } } LockSupport.park(); }, "线程2").start(); // 让线程2跑一下子 Thread.sleep(2000); // 线程3 new Thread(() -> { for (int i = 20; i < 40; i++) { TestBiasedLocking testBiasedLocking = list.get(i); synchronized (testBiasedLocking) { System.out.println("第" + (i + 1) + "次加锁-线程3"); System.out.println(ClassLayout.parseInstance(testBiasedLocking).toPrintable()); } } LockSupport.park(); }, "线程3").start(); // 让线程3跑一下子 Thread.sleep(2000); System.out.println("新出生的妹子"); System.out.println(ClassLayout.parseInstance(new TestBiasedLocking()).toPrintable()); LockSupport.park(); } }
下面咱们用通俗的语言来解释下rem
1) 线程一、1-50都结婚了(有偏向的线程了) 2) 线程二、1-19都离婚了(锁撤销),zf看不行啊,直接结婚证更名吧,20-40都换老公了(Thread Id直接换了) // 到达BiasedLockingBulkRebiasThreshold次数 3) 线程三、20-40又离婚了(锁撤销),滚蛋,都玩完,谁都不能结婚,结婚的也都给我离婚 (设置为不可偏向状态,正在运行的锁对象会被撤销)// 到达BiasedLockingBulkRevokeThreshold次数 4) 新出生的妹子,生下来就不让结婚。(new出来就是轻量级锁)
二婚的人持续时间超过-XX:BiasedLockingDecayTime=25000ms的话,撤销次数清为0,从新计算。
最后我来解释下文章中出现的举例get
怎么判断只有一次换老公的机会的(每一个对象是怎么判断只有重偏向的一次机会的?)
答:对象头中偏向锁有个Epoch,该对象对应的Class中也有这个字段,当可偏向的时候,Class中的Epoch+1,正在使用的锁对象Epoch也会+1(老公在家,没有机会换老公),判断可重偏向的时候,Class.Epoch != 对象.Epoch,表明可重偏向。
欢迎指正。