JVM的锁优化-锁升级

锁升级

  锁升级,是JDK1.6版本中对于synchronized的优化。调查发现一般情况下锁的使用都是为了处理一些极端情况,但多时间,并不会出现并发争强的情况,直接是有synchronized比较重,会影响系统性能。
  升级步骤: 无锁/匿名偏向锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
  升级特点:一旦升级,无法降级
image

锁状态对照表:(根据下图,可查对锁状态)
image

无锁

所有对象最初都是无锁状态。

public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }

image

偏向锁与轻量级锁

轻量级锁

  当第一线程获取到锁后,锁状态由无锁转为偏向锁。但实际发现锁状态是轻量级锁似乎没有偏向锁。~?!
  这其中涉及到jvm的偏向锁开启机制,5秒开始。5秒内,锁状态会由无锁转为轻量级锁。

    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
		// 获取锁
		// 此时锁,却是轻量级锁
        synchronized (o) {
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }

此时是轻量级锁
image

偏向锁

   添加5秒睡眠后,会出现偏向锁。此时会发现,偏向锁分为两钟,一个普通偏向锁,一个是匿名偏向锁。
  需要注意的是,当jvm开启偏向锁后,所有对象锁状态会自动转为匿名偏向锁,只会变更锁状态,不会记录线程信息。

    public static void main(String[] args) throws InterruptedException {
        // 5秒睡眠
        Thread.sleep(6000);
        Object o = new Object();
        // 此时锁为 匿名偏向级锁。即便是没有使用synchronized
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        // 此时锁为 偏向级锁
        synchronized (o) {
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }

image

重量级锁

  当轻量级锁进行CAS,当自旋达到一定次数后,锁升级为重量级锁。

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        Object o = new Object();
        // 此时锁是 匿名偏向锁
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

        new Thread(() -> {
            // 线程拿到锁后,锁状态变为偏向级锁
            synchronized (o) {
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();

        // 尝试获取锁后,发现是锁已经被获取,已是偏向级锁
        // 之后升级为 自旋锁,然后cas 一定次数后,升级为重量级锁
        synchronized (o) {
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
            // 如果cpu好的话,此时输出可能是轻量级锁
        }
    }

image


须知

5秒开始偏向锁设定原因 **

  偏向锁转化为轻量级锁时,会涉及到偏向锁撤销操作。查看ClassLoader源码发现,但在大量并发加载class的情况下,锁撤销会影响性能。为了避免添加了5秒开启,让锁状态直接从无锁转化为轻量级锁。

锁升级,但是否会降级 **

会,从匿名偏向锁会降级为无锁
匿名偏向锁状态下,如果系统调用hashCode()方法,需要将匿名偏向锁变为无锁状态,来存储HashCode。如果有线程获取锁,锁状态会直接从无锁转为轻量级锁,不会再升级为偏向锁。

锁升级流程

  • 无锁、匿名偏向锁: 当前对象没有作为锁存在
  • 偏向锁:如果当前锁资源,只有一个线程在频繁的获取与释放,那么这个线程过来,只需要判断,当前只想的线程是否是当前线程
    • 如果是,直接拿走资源
    • 如果不是,基于CAS的方式,尝试将偏向锁指向当前线程,如果获取不到,处方锁升级,升级为情况级锁
  • 轻量级锁:会采用自旋锁的方式频繁的以CAS的形式获取资源
    • 如果成功,拿走资源
    • 如果不成功,自旋了一定次数后,如果还不成功,就锁升级
  • 重量级锁:就是传统的synchronized方式,拿不到锁资源,就挂起当前线程

对象头信息参考图

Synchronized 是基于对象实现的锁,所以锁信息就放在对象头的MarkWork中。
image

重量级锁,是否还会CAS中


重量级锁是C写的,里面有关于CAS的使用,使用方式与java的相似。

posted @ 2023-10-10 11:12  之士咖啡  阅读(52)  评论(0编辑  收藏  举报