Java 中 synchronized 的使用方式和锁升级

 更新时间:2025年03月05日 14:26:52   作者:谢家小布柔  
Java中的synchronized关键字用于实现线程同步,保证同一时刻只有一个线程可以访问被同步的代码块或方法,JVM引入了锁升级机制,从无锁状态开始,根据竞争情况逐步升级为偏向锁、轻量级锁和重量级锁,以提高性能,感兴趣的朋友一起看看吧

在 Java 并发编程中,synchronized是一个非常重要的关键字,用于实现线程同步,保证在同一时刻只有一个线程可以访问被同步的代码块或方法,从而避免多线程带来的数据不一致等问题。同时,Java 虚拟机(JVM)为了提高ynchronized的性能,引入了锁升级机制。下面我们就来详细介绍ynchronized的使用和锁升级过程。

一、synchronized 的使用方式

(一)修饰普通方法

synchronized修饰一个普通方法时,锁对象是当前对象(this)。也就是说,当一个线程进入该方法时,会自动获取当前对象的锁,其他线程想要进入该方法,必须等待当前线程释放锁。例如:

public class SynchronizedExample {
    public synchronized void synchronizedMethod() {
        // 线程同步的代码逻辑
        System.out.println("线程 " + Thread.currentThread().getName() + " 进入同步方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 退出同步方法");
    }
}

在上述代码中,synchronizedMethod是一个同步方法,多个线程同时调用该方法时,会依次排队执行,保证了方法内代码的线程安全性。

(二)修饰静态方法

synchronized修饰静态方法时,锁对象是该类的Class对象。因为静态方法属于类级别,不依赖于具体的对象实例,所以使用类的Class对象作为锁。示例如下:

public class StaticSynchronizedExample {
    public static synchronized void staticSynchronizedMethod() {
        System.out.println("线程 " + Thread.currentThread().getName() + " 进入静态同步方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程 " + Thread.currentThread().getName() + " 退出静态同步方法");
    }
}

多个线程调用staticSynchronizedMethod时,会对类的Class对象进行加锁,从而实现线程同步。

(三)修饰代码块

synchronized还可以修饰代码块,此时需要显式指定锁对象。锁对象可以是任意对象,只要保证在需要同步的代码块中使用相同的锁对象即可。比如:

public class BlockSynchronizedExample {
    private Object lock = new Object();
    public void blockSynchronized() {
        synchronized (lock) {
            System.out.println("线程 " + Thread.currentThread().getName() + " 进入同步代码块");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程 " + Thread.currentThread().getName() + " 退出同步代码块");
        }
    }
}

在这个例子中,lock对象作为同步代码块的锁,多个线程访问blockSynchronized方法时,会竞争lock对象的锁。

二、synchronized 的锁升级

为了提高synchronized的性能,JVM 引入了锁升级机制,从无锁状态开始,根据竞争情况逐步升级为偏向锁、轻量级锁和重量级锁。

(一)无锁

无锁状态是最基本的状态,当没有线程竞争锁时,对象处于无锁状态。此时,线程可以直接访问被synchronized修饰的代码,无需进行任何加锁操作,因此性能最高。

(二)偏向锁

当一个线程首次访问被synchronized修饰的代码时,JVM 会将对象头中的锁标志位设置为偏向锁模式,并将该线程的 ID 记录在对象头中。此后,当该线程再次访问同一对象的同步代码时,无需进行额外的加锁操作,直接进入同步代码块,因为 JVM 认为该线程很可能会再次访问。偏向锁的存在减少了无竞争情况下的锁开销。

(三)轻量级锁

当有第二个线程试图访问同一个对象的同步代码时,偏向锁会升级为轻量级锁。JVM 会在当前线程的栈帧中创建一个锁记录(Lock Record),并将对象头中的 Mark Word 复制到锁记录中,然后尝试使用 CAS(Compare - And - Swap,比较并交换)操作将对象头的 Mark Word 替换为指向锁记录的指针。如果 CAS 操作成功,当前线程就获得了轻量级锁,可以进入同步代码块;如果失败,说明存在竞争,轻量级锁会升级为重量级锁。

(四)重量级锁

当轻量级锁竞争失败,即多个线程同时竞争锁时,会升级为重量级锁。此时,JVM 会使用操作系统的互斥量(Mutex)来实现锁机制,线程会进入阻塞状态,等待锁的释放。重量级锁的性能相对较低,因为线程的阻塞和唤醒需要操作系统的干预,会带来较大的开销。

锁升级的过程是 JVM 根据实际的线程竞争情况动态调整的,目的是在保证线程安全性的同时,尽可能提高synchronized的性能。

总之,synchronized是 Java 并发编程中实现线程同步的重要手段,理解其使用方式和锁升级机制,对于编写高效、安全的多线程程序具有重要意义。在实际开发中,我们应该根据具体的场景合理使用synchronized,并注意锁的粒度和性能优化,以充分发挥多线程的优势。

到此这篇关于Java 中 synchronized 的使用和锁升级的文章就介绍到这了,更多相关Java synchronized 使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot登陆页面图片验证码简单的web项目实现

    springboot登陆页面图片验证码简单的web项目实现

    这篇文章主要介绍了springboot登陆页面图片验证码简单的web项目实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-04-04
  • linux用java -jar启动jar包缓慢的问题

    linux用java -jar启动jar包缓慢的问题

    这篇文章主要介绍了linux用java -jar启动jar包缓慢的问题,具有很好的参考价值,希望对大家有所帮助,
    2023-09-09
  • Springboot与vue实例讲解实现前后端分离的人事管理系统

    Springboot与vue实例讲解实现前后端分离的人事管理系统

    这篇文章主要介绍了如何用Java实现企业人事管理系统,文中采用springboot+vue实现前后端分离,感兴趣的小伙伴可以学习一下
    2022-06-06
  • java CompletableFuture实现异步编排详解

    java CompletableFuture实现异步编排详解

    这篇文章主要为大家介绍了java CompletableFuture实现异步编排详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • SpringBoot中定时任务的使用方法解析

    SpringBoot中定时任务的使用方法解析

    这篇文章主要介绍了SpringBoot中定时任务的使用方法解析,@EnableScheduling 注解,它的作用是发现注解 @Scheduled的任务并由后台执行,没有它的话将无法执行定时任务,需要的朋友可以参考下
    2024-01-01
  • RocketMQ中消费者的消费进度管理

    RocketMQ中消费者的消费进度管理

    这篇文章主要介绍了RocketMQ中消费者的消费进度管理,业务实现消费回调的时候,当且仅当此回调函数返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS ,RocketMQ才会认为这批消息(默认是1条)是消费完成的,需要的朋友可以参考下
    2023-10-10
  • SpringBoot实现发送邮件功能过程图解

    SpringBoot实现发送邮件功能过程图解

    这篇文章主要介绍了SpringBoot实现发送邮件功能过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Spring Cache框架应用介绍

    Spring Cache框架应用介绍

    我们一定听说过"缓存无敌"的话,特别是在大型互联网公司,"查多写少"的场景屡见不鲜。Spring Cache是作用在方法上的,其核心思想是,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存在缓存中
    2022-09-09
  • 在eclipse中使用SVN的方法(图文)

    在eclipse中使用SVN的方法(图文)

    这篇文章主要介绍了在eclipse中使用SVN的方法(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • 往DAO类中注入@PersistenceContext和@Resource的区别详解

    往DAO类中注入@PersistenceContext和@Resource的区别详解

    这篇文章主要介绍了往DAO类中注入@PersistenceContext和@Resource的区别详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02

最新评论