高薪程序员必修课-Java中 Synchronized锁的升级过程

目录

前言

锁的升级过程

1. 偏向锁(Biased Locking)

原理:

示例:

2. 轻量级锁(Lightweight Locking)

原理:

示例:

3. 重量级锁(Heavyweight Locking)

原理:

示例:

注意事项

总结


前言

        在Java中,synchronized 关键字用于实现线程同步,确保在同一时间只有一个线程可以访问被保护的代码块。为了提高性能,Java对锁的实现进行了优化,采用了锁的升级机制。锁的升级过程包括从偏向锁、轻量级锁到重量级锁的升级。下面详细讲解锁的升级过程,并提供相应的示例。

锁的升级过程

  1. 偏向锁(Biased Locking)
  2. 轻量级锁(Lightweight Locking)
  3. 重量级锁(Heavyweight Locking)

1. 偏向锁(Biased Locking)

原理:

        偏向锁是为了优化只有一个线程访问同步块的情况。默认情况下,对象头中的标记字段(Mark Word)存储了偏向锁的线程ID。只有当另一个线程尝试获取锁时,偏向锁才会升级为轻量级锁。

示例:
public class BiasedLockingExample {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        synchronized (lock) {
            // 第一个线程获得偏向锁
            System.out.println("Thread 1 has acquired the biased lock.");
        }
    }
}

在这个示例中,synchronized 关键字使得 lock 对象在第一次被线程获取时进入偏向锁状态。

2. 轻量级锁(Lightweight Locking)

原理:

        当另一个线程尝试获取已偏向的锁时,偏向锁升级为轻量级锁。轻量级锁使用CAS(Compare-And-Swap)操作来避免阻塞。当竞争不激烈时,轻量级锁的性能优于重量级锁。

示例:
public class LightweightLockingExample {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                // 第一个线程获得偏向锁
                System.out.println("Thread 1 has acquired the lock.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                // 第二个线程尝试获取锁,偏向锁升级为轻量级锁
                System.out.println("Thread 2 has acquired the lock.");
            }
        });

        t1.start();
        Thread.sleep(100); // 确保t1先获得锁
        t2.start();

        t1.join();
        t2.join();
    }
}

在这个示例中,第二个线程尝试获取已经被第一个线程偏向的锁,导致偏向锁升级为轻量级锁。

3. 重量级锁(Heavyweight Locking)

原理:

        当多个线程频繁竞争同一个锁时,轻量级锁会不断地进行CAS操作,这时轻量级锁会升级为重量级锁(互斥锁)。重量级锁会使用操作系统的同步机制,使得其他线程在获取锁时进入阻塞状态,减少CPU的自旋消耗。

示例:
public class HeavyweightLockingExample {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                // 第一个线程获得锁
                System.out.println("Thread 1 has acquired the lock.");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                // 第二个线程尝试获取锁,锁升级为重量级锁
                System.out.println("Thread 2 has acquired the lock.");
            }
        });

        Thread t3 = new Thread(() -> {
            synchronized (lock) {
                // 第三个线程尝试获取锁,锁已是重量级锁
                System.out.println("Thread 3 has acquired the lock.");
            }
        });

        t1.start();
        Thread.sleep(100); // 确保t1先获得锁
        t2.start();
        Thread.sleep(100); // 确保t2在t1之后尝试获取锁
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }
}

在这个示例中,第三个线程尝试获取已经被第二个线程竞争的轻量级锁,导致轻量级锁升级为重量级锁。

注意事项

  1. 适用场景

    • 偏向锁适用于只有一个线程访问同步块的场景。
    • 轻量级锁适用于多个线程交替访问同步块且竞争不激烈的场景。
    • 重量级锁适用于多个线程频繁竞争同一个锁的场景。
  2. 锁升级不可逆

    • 一旦锁从偏向锁升级为轻量级锁或重量级锁,无法降级为偏向锁。为了性能优化,尽量避免频繁的锁竞争。
  3. 启用和禁用偏向锁

    • 默认情况下,偏向锁是启用的。如果需要禁用,可以在JVM启动参数中添加 -XX:-UseBiasedLocking

总结

        Java中 synchronized 锁的升级过程包括从偏向锁、轻量级锁到重量级锁:

  1. 偏向锁:优化单线程访问的场景,减少不必要的同步开销。
  2. 轻量级锁:通过CAS操作进行自旋,适用于竞争不激烈的场景。
  3. 重量级锁:使用操作系统同步机制进行阻塞,适用于高竞争的场景。

通过了解锁的升级过程,可以更好地优化并发程序的性能,避免不必要的锁竞争和性能开销。

⭐️ 好书推荐

《Effective Java中文版》

【内容简介】

        本书是Jolt获奖作品Effective Java的第3版,对上一版进行了全面更新,涵盖了从Java 5到Java 9的种种特性,是Java开发人员不可缺少的一本参考书。

        本书分为12章,包含90个条目,形式简洁。每个条目中都讲述了对Java的独到见解,阐明了如何编写高效、优雅的程序,并且提供了清晰、易懂的示例代码。与上一版相比,本书增加了Lambda表达式、流、Optional类、接口默认方法、try-with-resources、@SafeVarargs注解、模块等Java 7及以后所引入的新特性。本书介绍了如何充分利用泛型、枚举、注解、自动装箱、for-each循环、可变参数、并发机制等各种特性,帮助读者更加有效地使用Java编程语言及其基本类库(java.lang、java.util和java.io,以及子包,如java.util.concurrent和java.util.function等)。

📚 京东购买链接:【2024年全新译本】Effective Java中文版

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Javasynchronized升级是为了提高多线程并发执行的性能和效率。它通过在的使用过程进行优化和升级来实现。 在Javasynchronized升级主要涉及三个层面:Java层面、字节码层面和JVM层面(对象头)。 在Java层面上,synchronized升级包括以下几种状态: 1. 无状态:多个线程可以同时进入临界区,没有互斥的限制; 2. 偏向状态:当只有一个线程访问临界区时,偏向可以减少的竞争; 3. 轻量级状态:多个线程竞争同一个时,升级为轻量级,通过CAS操作来实现快速的加和解; 4. 重量级状态:多个线程竞争同一个时,升级为重量级,使用操作系统的互斥量来实现线程的阻塞和唤醒[1]。 在字节码层面上,synchronized同步代码块的升级实际上是通过字节码指令来实现的。当进入同步代码块时,会通过monitorenter指令获取,在退出同步代码块时,会通过monitorexit指令释放。这些指令可以保证临界区的原子性和互斥性,从而实现线程的同步。 在JVM层面上,synchronized升级是通过对象头的标记位来实现的。对象头的标记位包括了标志位、线程ID和指向记录的指针。通过这些标记位,JVM可以判断的状态和竞争情况,从而进行升级和降级。 总结来说,Javasynchronized升级是为了提高多线程并发执行的性能和效率。它通过在不同层面上对进行优化和升级来实现线程的同步和互斥,从而保证临界区的原子性和正确性。这些优化和升级包括了无状态、偏向状态、轻量级状态和重量级状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值