学习java的偏向锁,想用代码演示一下,结果被自动装箱坑了,记录一下
一、-128~127
@Test
public void test0() {
sleep(5);
Integer i = 1;
//无偏向
System.out.println(ClassLayout.parseInstance(i).toPrintable());
}
打印如下
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 67 22 00 f8 (01100111 00100010 00000000 11111000) (-134208921)
12 4 int Integer.value 1
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
二、Not In -128~127
@Test
public void test1() {
sleep(5);
Integer i = 555;
//有偏向
System.out.println(ClassLayout.parseInstance(i).toPrintable());
}
打印结果如下
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 67 22 00 f8 (01100111 00100010 00000000 11111000) (-134208921)
12 4 int Integer.value 555
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
三、JVM启动,前4秒创建的对象无偏向锁
@Test
public void test2() {
Integer i = 555;
sleep(5);
//无偏向
System.out.println(ClassLayout.parseInstance(i).toPrintable());
}
打印结果如下
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 67 22 00 f8 (01100111 00100010 00000000 11111000) (-134208921)
12 4 int Integer.value 555
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
四、偏向锁的实践
static class Foo {
private int i;
public synchronized void incr(String msg) {
i++;
System.out.println(msg + "<" + Thread.currentThread().getName() + ">\t\t\t" + ClassLayout.parseInstance(this).toPrintable());
}
}
@Test
public void test11() throws InterruptedException {
sleep(5);
Foo foo = new Foo();
System.err.println("初始状态\t" + ClassLayout.parseInstance(foo).toPrintable());
foo.incr("此时已经偏向main线程了");
System.err.println("此时没有线程竞争锁,但mark word不改变,main线程id依然存在\t" + ClassLayout.parseInstance(foo).toPrintable());
foo.incr("main线程继续获取锁,识别到是自己的线程id则直接进入锁");
Thread thread = new Thread(() -> foo.incr("此时第二个线程尝试获取锁"));
thread.start();
thread.join();
System.err.println("由上可知,只要有第二个线程尝试获取锁,[偏向锁]都会升级为[轻量级锁],而不是把mark word里的线程id改成第二个线程的;" +
"(理解偏向锁:只要有线程得到了偏向锁,那么这个锁一直归该线程所有,如果有其他线程竞争,立刻升级轻量级锁)");
System.err.println("锁居然释放了\t" + ClassLayout.parseInstance(foo).toPrintable());
foo.incr("锁释放后再获取锁,001->000轻量级锁");
}
打印结果如下
初始状态 com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
此时已经偏向main线程了<main> com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 30 c8 02 (00000101 00110000 11001000 00000010) (46673925)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 1
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
此时没有线程竞争锁,但mark word不改变,main线程id依然存在 com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 30 c8 02 (00000101 00110000 11001000 00000010) (46673925)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 1
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
main线程继续获取锁,识别到是自己的线程id则直接进入锁<main> com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 30 c8 02 (00000101 00110000 11001000 00000010) (46673925)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 2
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
此时第二个线程尝试获取锁<Thread-0> com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) c0 f2 e8 22 (11000000 11110010 11101000 00100010) (585691840)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 3
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
由上可知,只要有第二个线程尝试获取锁,[偏向锁]都会升级为[轻量级锁],而不是把mark word里的线程id改成第二个线程的;(理解偏向锁:只要有线程得到了偏向锁,那么这个锁一直归该线程所有,如果有其他线程竞争,立刻升级轻量级锁)
锁居然释放了 com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 3
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
锁释放后再获取锁,001->000轻量级锁<main> com.preon.demo.SynchronizedTest2$Foo object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) e0 e3 c7 02 (11100000 11100011 11000111 00000010) (46654432)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 24 24 01 f8 (00100100 00100100 00000001 11111000) (-134142940)
12 4 int Foo.i 4
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
未完待续…