关于java:并发系列三证明分代年龄无锁偏向锁轻量锁重chong偏向重chong轻量重量锁

6次阅读

共计 14759 个字符,预计需要花费 37 分钟才能阅读完成。

前言

  • 上篇文章咱们理解了 synchronized 关键字的常见用法、对象头以及证实了一个对象在无锁状态下的对象头 markwork 局部的前 56 位存储的是 hashcode。接下来,咱们持续来依据对象头别离证实分代年龄为什么是 15、无锁、偏差锁、轻量锁、重 (chong) 偏差、重 (chong) 轻量、分量锁,这些锁是实在存在的,咱们能够通过代码来重现。废话不多说,咱们一一来证实

一、证实分代年龄为什么为 15

  • 大家都晓得,在 jvm 中,若一个对象在 survivor 区通过了 15 次的 young gc。当再进行一次 young gc 时,这个对象将会挪动到老年代。那么为什么是 15 而不是 16、17、18 呢?这个问题就跟 hashmap 的初始容量为什么为 16 的起因有点类似,都波及到对象的二进制。咱们持续拿 java 对象头来阐明,请看下图:


由第一张图可知,分代年龄占用了 4bit,设想一下,4bit 能示意的最大数是什么?没错,就是所有的 bit 位都是 1,即 1111。而二进制的1111 转化成十进制后的值就是 15 啦。当初能明确分代年龄为什么是 15 了吧?

  • 利用此局部,咱们把图中形容的锁状态以表格的形式出现进去

    锁状态 锁标识 备注
    无锁 001 对象头中应用 baised_lock + lock 一共 3bit 来示意无锁和偏差锁的
    偏差锁 101 对象头中应用 baised_lock + lock 一共 3bit 来示意无锁和偏差锁的
    轻量锁 00 只用到了 lock 标识位
    分量锁 10 只用到了 lock 标识位
    GC 标记 11 只用到了 lock 标识位

二、证实对象处于无锁状态

  • 要证实这个很简略,间接创立一个 object 对象,并且应用 jol 打印进去对象头就能剖析出,请细看如下代码及运行后果
  • 第一步:创立 User.java

    package com.eugene.basic.concurrency.objectheader;
    
    public class User {}
  • 第二步:应用 JOL API 查看 user 对象的布局信息

    package com.eugene.basic.concurrency.objectheader;
    
    import org.openjdk.jol.info.ClassLayout;
    
    /**
     * 验证对象头 hashCode 信息
     */
    public class Valid {public static void main(String[] args) {User user = new User();
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
        }
    }
  • 运行后果如下图所示:

    上篇文章说了,自己电脑的 cpu 存储内存是以小端模式存储的,即 低位内存存储低位数据 。所以咱们只须要看红色框框的第一行的数据。第一行数据看哪呢?看value 局部,它的值为 00000001。依据咱们的 java 对象头结构图可知,从右边开始数,第 1 个 bit 是unused 局部、第 2 - 5 个 bit 是 分代年龄 局部、第 6 个 bit 是 biased_lock 偏差锁 标识、第 7 - 8 个 bit 是 lock 标识。由上剖析可知:最初两位的值为 01,而01 可能代表为无锁或者偏差锁,此时咱们再往前看一位,发现 biased_lock 位的值为 0. 因而最初三位值为001 ⇒ 证实 user 对象此时是无锁状态。

三、证实偏差锁

  • 证实偏差锁之前,咱们按下图操作,给 jvm 增加查看全局配置的参数:

    间接运行 main 办法,运行后果如下所示 (因为篇幅问题,只截图了要害局部)

    由图中的 -XX:BiasedLockingStartupDelay=4000 配置可知,jvm 会在启动虚拟机之后的 4s 后才会开启偏差锁性能。晓得这个概念后,咱们再来科普下什么是偏差锁。
  • 所谓偏差锁:即当一把锁处于 可偏差状态 时,当有线程持有这把锁后,这把锁将偏差于这个线程。这里提到了可偏差状态,何为 可偏差状态 呢?可偏差状态是指在 jvm 开启可偏差性能后,new 进去的一个对象它都是可偏差状态,即它的标识位为101,然而没有具体的偏差某一个线程。
  • 证实可偏差状态和偏差锁:

    增加如下代码并执行:

    public class Valid {public static void main(String[] args) throws InterruptedException {
            // 这里要留神, 肯定要在创建对象之前睡眠,若咱们先创建对象,能够想一想会产生什么状况!// 那必定是不会启动偏差锁的性能呀,咱们都晓得加锁其实是给对象加了个标识
            // 如果咱们在偏差锁性能未开启之前创立了对象,很道歉,// jvm 没有那么智能,前面不会去把这个对象改成可偏差状态(是偏差锁,然而没有偏差具体
            // 的线程)
            Thread.sleep(4100);
    
            System.out.println(ByteOrder.nativeOrder().toString());
            User user = new User();
            System.out.println("before lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
    
            synchronized (user) {System.out.println("lock ing");
                System.out.println(ClassLayout.parseInstance(user).toPrintable());
            }
    
            System.out.println("after lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
        }
    }
    > 查看运行后果

    四、证实一个对象调用了 hashcode 办法后无奈再被标识为偏差锁,而是升级成轻量锁

  • 编写如下代码(绝对于上述代码,仅在加锁前调用了对象的 hashcode 办法):

    public class Valid {public static void main(String[] args) throws InterruptedException {
            // 这里要留神, 肯定要在创建对象之前睡眠,若咱们先创建对象,能够想一想会产生什么状况!// 那必定是不会启动偏差锁的性能呀,咱们都晓得加锁其实是给对象加了个标识
            // 如果咱们在偏差锁性能未开启之前创立了对象,很道歉,// jvm 没有那么智能,前面不会去把这个对象改成可偏差状态(是偏差锁,然而没有偏差具体
            // 的线程)
            Thread.sleep(4100);
    
            System.out.println(ByteOrder.nativeOrder().toString());
            User user = new User();
            System.out.println("before lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
    
            System.out.println(user.hashCode());
    
            synchronized (user) {System.out.println("lock ing");
                System.out.println(ClassLayout.parseInstance(user).toPrintable());
            }
    
            System.out.println("after lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
        }
    }
    
  • 运行后果及剖析

    五、证实轻量锁

  • 这里说下轻量锁的概念:若线程是交替执行的,即上一个线程执行完开释锁后下一个线程再获取锁。若在 jvm 未开启偏差锁的过程中,对对象进行加锁时,对象间接是轻量锁。
  • 撰写如下代码并执行:

    public class Valid {public static void main(String[] args) throws InterruptedException {System.out.println(ByteOrder.nativeOrder().toString());
    
            User user = new User();
            System.out.println("before lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
    
            synchronized (user) {System.out.println("lock ing");
                System.out.println(ClassLayout.parseInstance(user).toPrintable());
            }
    
            System.out.println("after lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
        }
    }
  • 运行后果如下

    六、证实偏差锁收缩为轻量锁

  • 编写如下代码并执行

    public class Valid {public static void main(String[] args) throws InterruptedException {
            // 开启偏差锁性能
            Thread.sleep(4100);
            System.out.println(ByteOrder.nativeOrder().toString());
    
            User user = new User();
            System.out.println("before lock" + ClassLayout.parseInstance(user).toPrintable());
    
            synchronized (user) {System.out.println("lock ing" + ClassLayout.parseInstance(user).toPrintable());
            }
    
            System.out.println("after lock" + ClassLayout.parseInstance(user).toPrintable());
    
            // 开启线程来获取锁
            Thread t1 = new Thread(() -> {synchronized (user) {System.out.println("other t1 thread get lock" + ClassLayout.parseInstance(user).toPrintable());
                }
            }, "t1");
            t1.start();
            // 期待 t1 执行完后再打印一次锁信息
            t1.join();
    
            System.out.println("after t1 thread release lock" + ClassLayout.parseInstance(user).toPrintable());
        }
    }
  • 运行后果及剖析

    七、证实重 (chong) 偏差

  • 持续援用第三章:证实偏差锁的图

    咱们关注 intx BiasedLockingBulkRebiasThreshold = 20 此配置。此配置阐明整个偏差锁重偏差的阈值为 20。ok,阈值咱们晓得了,接下来阐明下什么叫做重偏差
  • 所谓重偏差,依照字面意思来了解就是:锁的重偏差过程。然而大家都晓得,锁的状态是不可逆的,当偏差锁被其余线程持有后就会收缩成轻量锁了。然而,这里的重偏差是指批量重偏差。咱们先来看例子再来总结:
  • 编写如下类,并运行它:

    public class ReBiasedLock {static List<User> locks = new ArrayList<>();
    
        static final int THREAD_COUNT = 19;
    
        public static void main(String[] args) throws InterruptedException {
            // 提早 4.1 秒,期待 jvm 偏差锁性能开启
            Thread.sleep(4300);
    
            // 线程 1 中的锁全为偏差锁。Thread t1 = new Thread(() -> {for (int i = 0; i < THREAD_COUNT; i++) {User lock = new User();
                    locks.add(lock);
                    synchronized (lock) {System.out.println("线程 1 第" + (i + 1) + "把锁");
                        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
                        System.out.println("\n *********************************** \n");
                    }
    
                }
            }, "线程 1");
    
            t1.start();
            // 等 t1 执行完
            t1.join();
    
            // 增加一个新线程,防止出现偏差锁的 id 反复的状况
            // 我也不晓得为什么,只晓得这样能解决这样的问题
            Thread tmp = new Thread(() -> {System.out.println(1);
            }, "tmp");
            tmp.start();
    
            new Thread(() -> {for (int i = 0; i < locks.size(); i++) {User lock = locks.get(i);
                    synchronized (lock) {System.out.println("线程 2 第" + (i + 1) + "把锁");
                        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
                        System.out.println("\n ==================================== \n");
                    }
                }
            }, "线程 2").start();}
    }
    > ** 当线程数量 THREAD_COUNT=19 时 **,第一个循环执行结束后,线程 list 中的 user 对象全副为偏差锁,偏差于线程 1。第二个线程执行结束后,list 中的 user 对象全副收缩成轻量锁。这里查看下第一次和第二次循环的局部输入
    ![在这里插入图片形容](https://img-blog.csdnimg.cn/20200528113134512.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2F2ZW5nZXJFdWc=,size_16,color_FFFFFF,t_70)

  • 咱们测试另一种状况,把 THREAD_COUNT 改成 25 并执行它

    > ** 当线程数量 THREAD_COUNT=25 时 **,第一个循环执行结束后,同上,list 中的 user 对象全副为偏差锁。第二个循环执行完后,前 19 把锁是轻量锁,从第 20 把锁开始,及其前面的所有的锁,都变成了偏差锁,从新偏差成了线程 2。![在这里插入图片形容](https://img-blog.csdnimg.cn/20200528114057368.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2F2ZW5nZXJFdWc=,size_16,color_FFFFFF,t_70)

  • 论断:当同一类型的锁被同一个线程收缩轻量锁的次数达到了 20,那么会将后续的同一类型的锁对立重偏差到以后线程。

八、证实重 (chong) 轻量

  • 持续援用第三章:证实偏差锁的图

    咱们关注 intx BiasedLockingBulkRevokeThreshold = 40 配置。此配置阐明整个重轻量的阈值为 40。ok,阈值咱们晓得了,接下来阐明下什么叫做重轻量
  • 重轻量概念:若同一类型的锁降级轻量锁的次数达到了 40,此时就会将前面的锁都批量撤销为无锁状态,并收缩到轻量锁
  • 咱们新增如下代码并运行它:

    public class ReLightweightLock {static List<User> locks = new ArrayList<>();
    
        public static void main(String[] args) throws InterruptedException {System.out.println("Starting");
    
            // 提早加载,让 jvm 开启偏差锁性能
            Thread.sleep(4400);

        Thread t1 = new Thread(() -> {for (int i = 0; i < 45; i++) {User lock = new User();
                locks.add(lock);
                synchronized (lock) {// 不做任何事,能够确定 45 把锁全副变成了偏差锁}
            }
        }, "t1");
        t1.start();
        t1.join();

        // 打印第 43 把锁,曾经是偏差锁了
        System.out.println("i = 42 \t" + ClassLayout.parseInstance(locks.get(42)).toPrintable());

        // 创立一个新线程睡眠 2s,保障上面的代码先执行,保障重偏差时,不会呈现线程 ID 反复的状况
        new Thread(() -> {
            try {Thread.sleep(2000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }, "tmp1").start();

        Thread t2 = new Thread(() -> {for (int i = 0; i < locks.size(); i++) {User lock = locks.get(i);
                synchronized (lock) {if (i == 10 || i == 21) {
                        // 输入第 11 和 22 个,看看别离是不是轻量锁和偏差锁
                        System.out.println("t2 i =" + i + "\t" + ClassLayout.parseInstance(lock).toPrintable());
                    }
                }
            }
        }, "t1");
        t2.start();
        t2.join();

        // 查看第 11 把锁对象,看看是不是 20 之前的锁也被重偏差了  --> 后果证实,只会对 20 当前的锁重偏差
        // 这里输入的是无锁状态,因为 i = 10 时,被线程 2 持有过,收缩成轻量锁了,而轻量锁在开释锁后会变成无锁状态
        System.out.println("i = 10\t" + ClassLayout.parseInstance(locks.get(10)).toPrintable());

        // 查看第 43 把锁对象,看看是不是被批量重偏差了  --> 后果证实:是的
        System.out.println("i = 42\t" + ClassLayout.parseInstance(locks.get(42)).toPrintable());


        // 创立一个新线程睡眠 2s,保障上面的代码先执行,保障重偏差时,不会呈现线程 ID 反复的状况
        new Thread(() -> {
            try {Thread.sleep(2000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }, "tmp2").start();

        Thread t3 = new Thread(() -> {for (int i = 0; i < locks.size(); i++) {User lock = locks.get(i);
                synchronized (lock) {if (i == 10 || i == 21 || i == 40) {
                        // 输入第 11 和 22 个,看看是不是都为轻量锁
                        // ---> 后果证实:都为轻量锁
                        // i == 10 为轻量锁,咱们都能了解,因为偏差锁被其余线程持有了,当然收缩为轻量锁了
                        // 可是 i == 21 不应该为偏差锁么?(超过了重偏差的阈值)
                        // ==> 这里不是重偏差了,因为 user 类型的锁降级为轻量锁的次数达到了 40(线程 2 降级了 20 次),// 所以 jvm 间接做了重轻量的操作,把前面所有的锁都变成轻量锁了
                        // 所以 i == 21 应该是轻量锁
                        // i == 40 同样也是轻量锁
                        System.out.println("t3 i =" + i + "\t" + ClassLayout.parseInstance(lock).toPrintable());
                    }
                }
            }
        }, "t3");
        t3.start();
        t3.join();

        // 此时是无锁状态,因为线程 3 进行批量重轻量了,而它开释了锁,所以是无锁状态
        System.out.println("main i = 40 \t" + ClassLayout.parseInstance(locks.get(40)).toPrintable());

    }
}
```
剖析后果在正文上曾经有了,能够依据正文信息和上面的运行后果来做比对
```txt
Starting
i = 42     com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 f8 a1 29 (00000101 11111000 10100001 00101001) (698480645)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t2 i = 10    com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           e8 f5 69 2a (11101000 11110101 01101001 00101010) (711587304)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t2 i = 21    com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 59 a8 29 (00000101 01011001 10101000 00101001) (698898693)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

i = 10    com.eugene.basic.concurrency.objectheader.User 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

i = 42    com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 59 a8 29 (00000101 01011001 10101000 00101001) (698898693)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t3 i = 10    com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           c8 ee 89 2a (11001000 11101110 10001001 00101010) (713682632)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t3 i = 21    com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           c8 ee 89 2a (11001000 11101110 10001001 00101010) (713682632)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t3 i = 40    com.eugene.basic.concurrency.objectheader.User object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           c8 ee 89 2a (11001000 11101110 10001001 00101010) (713682632)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

main i = 40     com.eugene.basic.concurrency.objectheader.User 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


Process finished with exit code 0

```

九、证实分量锁

  • 分量锁概念:多个线程存在强烈的竞争时,锁会收缩成分量锁,且不可逆!
  • 典型案例:生产者消费者模型:

    public class ValidSynchronized {static Object lock = new Object();
    
        static volatile LinkedList<String> queue = new LinkedList<>();
    
        public static void main(String[] args) throws InterruptedException {System.out.println("before lock");
            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
    
            Consumer consumer = new Consumer();
            Producer producer = new Producer();
    
            consumer.start();
            producer.start();
    
            Thread.sleep(500);
            consumer.interrupt();
            producer.interrupt();
    
            // 睡眠 3s ==> 目标是为了让锁本人开释,避免在开释过程中打印锁的状态呈现分量锁的状况
            Thread.sleep(3000);
            System.out.println("after lock");
            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
        }
    }
    
    class Producer extends Thread {
    
        @Override
        public void run() {while (!isInterrupted()) {synchronized (ValidSynchronized.lock) {System.out.println("lock ing");
                    System.out.println(ClassLayout.parseInstance(ValidSynchronized.lock).toPrintable());
                    String message = UUID.randomUUID().toString();
                    System.out.println("生产者生产音讯:" + message);
                    ValidSynchronized.queue.offer(message);
                    try {
                        // 生产者本人 wait,目标是开释锁
                        ValidSynchronized.lock.notify();
                        ValidSynchronized.lock.wait();
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {this.interrupt();
                    }
                }
            }
        }
    }
    
    class Consumer extends Thread {
    
        @Override
        public void run() {while (!isInterrupted()) {synchronized (ValidSynchronized.lock) {if (ValidSynchronized.queue.size() == 0) {
                        try {ValidSynchronized.lock.wait();
                            ValidSynchronized.lock.notify();} catch (InterruptedException e) {e.printStackTrace();
                        }
                    }
                    String message = ValidSynchronized.queue.pollLast();
                    System.out.println("消费者生产音讯:" + message);
    
                    try {TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {this.interrupt();
                    }
                }
            }
        }
    }
    运行后果:

    十、证实调用 wait 办法后,锁会降级为分量锁

  • 运行如下代码:

    public class ValidWait {public static void main(String[] args) throws InterruptedException {Thread.sleep(4100);
    
            final User user = new User();
    
            System.out.println("before lock");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
    
            Thread t1 = new Thread(() -> {synchronized (user) {System.out.println("lock ing");
                    System.out.println("before wait");
                    System.out.println(ClassLayout.parseInstance(user).toPrintable());
    
                    try {user.wait();
                        System.out.println("after wait");
                        System.out.println(ClassLayout.parseInstance(user).toPrintable());
                    } catch (InterruptedException e) {e.printStackTrace();
                    }
                }
    
            }, "t1");
            t1.start();
    
            // 主线程睡眠 3s 后,唤醒 t1 线程
            Thread.sleep(3000);
            System.out.println("主线程查看锁,变成了分量锁");
            System.out.println(ClassLayout.parseInstance(user).toPrintable());
        }
    }
  • 运行后果如下

    十一、总结

  • 偏差锁和 hashcode 是互斥的,只能存在一个
  • jvm 默认对偏差锁性能是提早加载的,大略工夫为 4s 钟 ,能够增加 JVM 参数:-XX:BiasedLockingStartupDelay=0 来设置延迟时间为 0。偏差锁的提早加载敞开后,基本上所有的锁都会为 可偏差状态,即 mark word 为 101,然而它还没有具体偏差的线程信息
  • 偏差锁退出同步块后仍然也是偏差锁
  • 重量级锁之所以分量就是因为状态不停的切换,最终映射到代码层面就是不停的调用操作系统函数 (最终会调用到 jvm 的mutex 类)
  • 调用锁对象的 wait 办法时,以后锁对象会立马降级为重量级锁
  • 偏差锁只有被其余线程拿到了,此时偏差锁会收缩。收缩为 轻量锁
  • 并发模块对应 github 地址:传送门
  • I am a slow walker, but I never walk backwards.

正文完
 0