批量重偏向:当一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,会导偏向锁重偏向的操作。 批量撤销:在多线程竞争剧烈的情况下,使用偏向锁将会降低效率,于是乎产生了批量撤销机制。 JVM的默认参数值:
测试代码:
public static void main(String[] args) throws InterruptedException {
//延时产生可偏向对象
Thread.sleep(5000);
//创造50个偏向线程t1的偏向锁
List<A> listA = new ArrayList<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
A a = new A();
synchronized (a) {
listA.add(a);
}
}
try {
//为了防止JVM线程复用,在创建完对象后,保持线程t1状态为存活
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
//睡眠3s钟保证线程t1创建对象完成
Thread.sleep(3000);
System.out.println("打印t1线程,list中第20个对象的对象头:");
System.out.println((ClassLayout.parseInstance(listA.get(19)).toPrintable()));
//创建线程t2竞争线程t1中已经退出同步块的锁
Thread t2 = new Thread(() -> {
//这里面只循环了30次!!!
for (int i = 0; i < 30; i++) {
A a = listA.get(i);
synchronized (a) {
//分别打印第19次和第20次偏向锁重偏向结果
if (i == 18 || i == 19) {
System.out.println("打印t2线程,第" + (i + 1) + "次偏向结果");
System.out.println((ClassLayout.parseInstance(a).toPrintable()));
}
}
}
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中第41个对象的对象头:");
System.out.println((ClassLayout.parseInstance(listA.get(40)).toPrintable()));
}
输出结果:
打印t1线程,list中第20个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 c8 35 69 (00000101 11001000 00110101 01101001) (1765132293)
...
这时候所有的对象a都是偏向线程1的,打印出偏向锁没有问题。
00000101
后三位是101
打印t2线程,第19次偏向结果
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 e8 47 0a (11111000 11101000 01000111 00001010) (172484856)
...
t2与t1竞争,偏向锁升级为轻量锁,
11111000
后三位是000
打印t2线程,第20次偏向结果
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 81 34 69 (00000101 10000001 00110100 01101001) (1765048581)
...
前面19个a都偏向锁升级为轻量锁,达到重偏向阈值,第20个a直接重偏向到t2
打印list中第11个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
...
第11个a作为轻量锁走出同步块后,进入无锁状态,
00000001
后三位是001
。
打印list中第26个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 81 34 69 (00000101 10000001 00110100 01101001) (1765048581)
...
t2竞争了前30个对象a,第0~18个对象a未达到重偏向阈值,是轻量锁,待走出t2同步块后进入无锁状态。第19~29发生重偏向,是偏向锁。
打印list中第41个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 c8 35 69 (00000101 11001000 00110101 01101001) (1765132293)
...
第30~49没有发生变化 还是偏向锁
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 <50 ; 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()));
System.out.println("再次打印list中第50个对象的对象头:");
System.out.println((ClassLayout.parseInstance(listA.get(49)).toPrintable()));
}
打印list中第11个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
...
第0~19个对象a没有重偏向,走出t2同步块后由轻量锁变无锁
001
打印list中第26个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 41 10 eb (00000101 01000001 00010000 11101011) (-351256315)
...
第20~39重偏向,仍是偏向锁
101
打印list中第50个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 38 10 eb (00000101 00111000 00010000 11101011) (-351258619)
...
第40~49仍是偏向t1
thread3 第20次
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 78 eb 10 (11111000 01111000 11101011 00010000) (283867384)
...
thread3 第22次
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 78 eb 10 (11111000 01111000 11101011 00010000) (283867384)
...
此时经过t2竞争前40个a,已经到了批量撤销的阈值40,而第20~39已经重偏向过t2,不会再次重偏向,所以升级为轻量锁。(偏向锁重偏向一次之后不可再次重偏向。)
重新输出新实例A
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
...
打印list中第50个对象的对象头:
A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 d8 88 33 (00000101 11011000 10001000 00110011) (864606213)
...
JVM 看到类A已经经历过批量撤销,剥夺了A的新实例对象使用偏向锁的权利;第50个对象a始终没有被t2 t3竞争过,还是偏向t1。(批量重偏向和批量撤销是针对类的优化,和对象无关。)