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中不同的锁实现方式,并根据实际场景选择最适合的锁。

🍂最后

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

相关推荐
爪哇学长2 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
ExiFengs6 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj1234567898 分钟前
JDK1.8新增特性
java·开发语言
繁依Fanyi19 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子30 分钟前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m512734 分钟前
LinuxC语言
java·服务器·前端
IU宝39 分钟前
C/C++内存管理
java·c语言·c++
瓜牛_gn39 分钟前
依赖注入注解
java·后端·spring
hakesashou40 分钟前
Python中常用的函数介绍
java·网络·python
佚先森1 小时前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html