JVM锁优化:Java原生锁的背后!

《Java零基础教学》是一套深入浅出的 Java 编程入门教程。全套教程从Java基础语法开始,适合初学者快速入门,同时也从实例的角度进行了深入浅出的讲解,让初学者能够更好地理解Java编程思想和应用。

本教程内容包括数据类型与运算、流程控制、数组、函数、面向对象基础、字符串、集合、异常处理、IO 流及多线程等 Java 编程基础知识,并提供丰富的实例和练习,帮助读者巩固所学知识。本教程不仅适合初学者学习,也适合已经掌握一定 Java 基础的读者进行查漏补缺。

🍂1. 前言

在多线程编程中,锁是一个非常重要的话题。Java原生提供了synchronized关键字和Lock接口等方式来实现锁。而JVM在处理锁时,也做了不少优化。本文将阐述JVM对Java的原生锁做了哪些优化。

🍂2. 摘要

本文首先简单介绍了Java中锁的两种实现方式:synchronized和Lock接口。然后分别从JVM级别进行解析:synchronized的优化主要包括:偏向锁、轻量级锁和重量级锁等;而Lock接口的优化主要包括:公平锁和非公平锁等。本文最后给出了相应的代码示例和测试用例,最终得出结论:JVM对Java的原生锁做了很多优化,这些优化大大提升了锁的性能和效率。

🍂3. 正文

🌲3.1 Java中锁的实现方式

在Java中,锁主要有两种实现方式:synchronized和Lock接口。

🍁3.1.1 synchronized

synchronized关键字是Java中最基本的锁实现方式。它是在JVM层面实现的,具有自动释放锁、重入锁和可见性等特性,使用简单方便。

🍁3.1.2 Lock接口

Lock是JDK提供的高级锁机制,它的实现方式是基于Java中的AQS(AbstractQueuedSynchronizer)类实现的。AQS是一个同步框架,它可以用于构建同步器,其中包括ReentrantLock和ReentrantReadWriteLock等。

🌲3.2 synchronized的优化

JVM对synchronized锁进行了很多优化,主要包括以下三种:

🍁3.2.1 偏向锁

偏向锁:这种优化是JVM为了减少同步无竞争情况下的性能消耗而引入的。当一个线程访问同步块并获取锁时,JVM会在对象头和线程栈上做一个记录,记录下线程ID、对象头指针和锁标志。当这个线程持有锁的时间超过一定阈值时,JVM会将锁升级为轻量级锁;当线程竞争锁时,锁会直接升级为重量级锁。

🍁3.2.2 轻量级锁

轻量级锁:这种优化是为了在低竞争条件下,减少锁的开销而引入的。当一个线程请求锁时,JVM会优先使用CAS(Compare And Swap)操作将对象头指针指向线程栈上的锁记录,如果CAS操作成功,则表示该线程获取到了锁,并且锁的状态改变为轻量级锁状态;如果CAS操作失败,则表示有其他线程竞争此锁,线程会进入自旋,不需要系统调用,因此效率极高。

🍁3.2.3 重量级锁

重量级锁:这种优化是为了在高竞争条件下,保证线程竞争公平而引入的。如果一个线程尝试获取锁时,发现锁已经被其他线程持有,它会陷入阻塞状态,直到锁被释放。

🌲3.3 Lock接口的优化

Lock接口相比于synchronized关键字,优化点更多,主要包括以下两种:

🍁3.3.1 公平锁

公平锁:在多个线程竞争同一个锁时,如果锁是公平锁,则每个线程都会按照它们发出请求的时间顺序来获取锁,这种情况下线程获取锁的顺序是有序的。

🍁3.3.2 非公平锁

非公平锁:在多个线程竞争同一个锁时,如果锁是非公平锁,则不保证线程获取锁的顺序,这样可能会导致某些线程一直无法获取到锁。

🌲3.4 代码示例和测试用例

下面给出了对于synchronized和Lock接口的相应代码示例和测试用例:

🍁3.4.1 synchronized实现的锁机制

示例代码如下:

java 复制代码
public class SyncJava {

    //初始为0
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized void decrement() {
        count--;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        SyncJava sync = new SyncJava();
        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 0; i < 1000; i++) {
            executor.execute(() -> {
                sync.increment();
                sync.decrement();
            });
        }

        //关闭线程池
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);

        System.out.println(sync.getCount());
    }
}

测试用例说明:创建一个SyncJava实例,实现了increment()方法和decrement()方法,其中使用了synchronized关键字保证线程安全。创建一个线程池,启动1000个线程,每个线程执行一次increment()方法和一次decrement()方法。最终输出count的值,究竟该count值会是多少呢?咱们拭目以待。

实际运行截图如下:

由于synchronized关键字保证了线程安全,最终count值只会跟初始值一样,为 0,实际上输出结果也跟预期结果是一致的。

🍁3.4.2 Lock接口实现的锁机制

示例代码如下:

java 复制代码
public class LockTest {

    private Lock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public void decrement() {
        lock.lock();
        try {
            count--;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        LockTest demo = new LockTest();
        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 0; i < 1000; i++) {
            executor.execute(() -> {
                demo.increment();
                demo.decrement();
            });
        }

        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);

        System.out.println(demo.getCount());
    }
}

测试用例说明:创建一个LockTest实例,实现了increment()方法和decrement()方法,其中使用了Lock接口来实现线程安全。与上述测试保持一致,也是创建一个线程池,启动1000个线程,每个线程执行一次increment()方法和一次decrement()方法,最终测试输出count的值。

实际运行截图如下: 很明显,结果为0,这就验证了多线程中安全执行increment()和decrement()方法,每个线程都会按照它们发出请求的时间顺序来获取锁,哪怕执行一千次一万次,count值也是顺序一加一减,最后count值只能为0.

🍂4. 结论

总的来说,JVM对Java的原生锁进行了很多优化,主要包括偏向锁、轻量级锁、重量级锁、公平锁和非公平锁等。这些优化大大提升了锁的性能和效率。在实际编程中,我们需要了解Java中不同的锁实现方式,并根据实际场景选择最适合的锁。

🍂最后

大家如果觉得看了本文有帮助的话,麻烦给个三连(点赞、分享、转发)支持一下哈。

相关推荐
尚学教辅学习资料1 分钟前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理
雷神乐乐17 分钟前
File.separator与File.separatorChar的区别
java·路径分隔符
小刘|22 分钟前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
逊嘘41 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
morris1311 小时前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员1 小时前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU1 小时前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie61 小时前
在IDEA中使用Git
java·git
Elaine2023912 小时前
06 网络编程基础
java·网络