JAVA 偏向锁

本文通过代码示例深入探讨了Java中的偏向锁机制,展示了-128到127之间的Integer对象优化以及在不同场景下偏向锁的行为。在实践中,当线程连续持有偏向锁时,会提升效率;而一旦有其他线程尝试获取锁,偏向锁将升级为轻量级锁。文章揭示了Java内存模型中锁的微妙之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习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

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值