并发编程(十五)Synchronized锁升级-偏向锁(下)-例1

55 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情

1.偏向锁竞争

假设一种情况A线程获取偏向锁,并且A线程死亡退出。B线程争抢偏向锁,此时B线程的锁状态是什么样子的? 我给大家举个例子:

public class SyncSyncLockRelease {
    static Thread A;
    static Thread B;
    public static void main(String[] args) {
        final List<Object> list = new ArrayList<>();
        A = new Thread() {
            @SneakyThrows
            @Override
            public void run() {
                Object a = new Object();
                list.add(a);
                System.out.println("AAAA加锁前" + ClassLayout.parseInstance(a).toPrintable());
                synchronized (a) {
                    System.out.println("AAAA加锁中" + ClassLayout.parseInstance(a).toPrintable());
                }
                System.out.println("AAAA加锁后" + ClassLayout.parseInstance(a).toPrintable());
                //防止竞争 执行完后唤醒线程B/ 确保A线程 死亡 Terminated
                LockSupport.unpark(B);
            }
        };
        B = new Thread() {
            @Override
            public void run() {
                LockSupport.park();
                Object a = list.get(0);
                System.out.println("线程BBBB加锁前" + ClassLayout.parseInstance(a).toPrintable());
                synchronized (a) {
                    System.out.println("线程BBBB加锁中" + ClassLayout.parseInstance(a).toPrintable());
                }
                System.out.println("线程BBBB加锁后" + ClassLayout.parseInstance(a).toPrintable());
                System.out.println("新产生的对象" + ClassLayout.parseInstance(new Object()).toPrintable());
            }
        };
        A.start();
        B.start();
    }
}

执行效果如下:

AAAA加锁前java.lang.Object 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)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

AAAA加锁中java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 50 5c 20 (00000101 01010000 01011100 00100000) (542920709)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

AAAA加锁后java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 50 5c 20 (00000101 01010000 01011100 00100000) (542920709)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

线程BBBB加锁前java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 50 5c 20 (00000101 01010000 01011100 00100000) (542920709)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

线程BBBB加锁中java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           58 f4 b8 21 (01011000 11110100 10111000 00100001) (565769304)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

线程BBBB加锁后java.lang.Object 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)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

新产生的对象java.lang.Object 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)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

我们一块一块分析。

当程序开始执行时,AB线程都开启后,B线程由于LockSupport.park()处于阻塞状态,A线程执行效果如下,说明: image.png

image.png

image.png

当A线程执行完毕后,LockSupport.unpark(B)唤醒B线程,此时B线程开始有打印结果,我们继续分析:

image.png

image.png

image.png

这时我们可以看到,线程B加锁中为轻量级锁,加锁后为无锁,这是不是就代表着锁降级了呢?

不是的,synchronize锁只有升级没有降级,因为我们的锁升级完全是jvm控制,那么锁降级主要是没法掌控降级的临界点,把握不好尺度,不知道如何高效的降级或者降到什么级别。这里是轻量级锁释放了锁,此时这个对象就是无锁状态。

image.png 如果再新建一个对象,我们可以看到新产生的对象是偏向锁状态,并且是否为偏向锁前面都是0。

2.总结

A线程获取偏向锁,并且A线程死亡退出。B线程争抢偏向锁,会直接升级当前对象的锁为轻量级锁。这只是针对我们争抢了一次。