Java关键字synchronized基本使用详解

 更新时间:2024年01月03日 08:23:52   作者:小尘要自信  
这篇文章主要给大家介绍了关于Java关键字synchronized基本使用的相关资料,synchronized可以用来同步静态和非静态方法,文中通过代码介绍的非常详细,需要的朋友可以参考下

基本使用

Java中的synchronized关键字用于在多线程环境下确保数据同步。它可以用来修饰方法和代码块

当一个线程访问一个对象的synchronized方法或代码块时,其他线程将无法访问该对象的其他synchronized方法或代码块。这样可以确保在同一时间只有一个线程能够执行该代码块或方法,避免了多线程环境下的数据不一致问题,例如:

public class SynchronizedExample {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
}

在上面的代码中,increment()方法是一个synchronized方法。当多个线程访问这个方法时,只有一个线程能够执行该方法的代码,其他线程将被阻塞。
synchronized关键字也可以用来修饰代码块,如:

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

在上面的代码中,synchronized关键字修饰的是一个代码块,并且锁对象是当前对象(this)

注意:synchronized关键字会导致线程上下文切换和资源竞争,所以在使用时要注意性能问题

源码解析

底层实现是通过 Java 虚拟机(JVM)的对象头和监视器锁机制实现的

具体来说,当一个线程访问一个对象的 synchronized 方法或代码块时,它会试图获取该对象的监视器锁。如果该锁未被其他线程占用,该线程将获得该锁并执行代码;如果该锁被其他线程占用,该线程将进入阻塞状态,等待获取该锁

synchronized 是Java中用于实现同步的关键字,它在底层通过监视器锁(Monitor)来实现。下面是synchronized的源码解析:

在Java中,每个对象都有一个与之关联的监视器锁,也称为内置锁或对象锁。当线程进入一个synchronized方法或代码块时,它会尝试获取该对象的监视器锁。如果锁没有被其他线程占用,则该线程获得锁并开始执行代码;如果锁已经被其他线程占用,则该线程将被阻塞,直到锁被释放。

在Java虚拟机中,每个对象头中都包含一部分用于实现synchronized的相关信息。这些信息包括:

  • mark word:用于存储对象的标记信息,包括锁的状态。
  • Klass pointer:指向对象的类元数据,包括synchronized的相关信息。
  • monitor:与对象关联的监视器,它记录了当前占用锁的线程、等待锁的线程队列等。

当一个线程尝试获取一个对象的锁时,虚拟机会检查对象头中的标记信息。如果对象的锁状态为无锁状态,即未被其他线程占用,则该线程可以获取锁,并将标记信息设置为锁定状态。如果对象的锁状态为已锁定,并且当前线程是锁的所有者,则该线程可以继续执行代码。如果对象的锁状态为已锁定,并且当前线程不是锁的所有者,则该线程将被放入等待队列中,进入阻塞状态。

当持有锁的线程执行完synchronized方法或代码块后,它会释放锁,即将对象头中的锁状态置为无锁状态,并唤醒等待队列中的一个线程,使其获取锁并继续执行。

需要注意的是,synchronized关键字可以修饰方法和代码块。在方法上修饰的synchronized表示对整个方法进行同步,而在代码块上修饰的synchronized表示对该代码块进行同步,使用的锁对象通常是方法所属对象或指定的对象。

总结起来,通过监视器锁的机制,Java的synchronized能够保证同一时刻只有一个线程访问同步代码块或方法,避免了多线程的数据竞争和并发问题。

这里给出一份简化的 synchronized 关键字的源码:

public void synchronized method() {
    // 加锁
    Monitor.enter(this);
    try {
        // 同步代码块
    } finally {
        // 释放锁
        Monitor.exit(this);
    }
}

在这份代码中,方法通过调用 Monitor.enter 方法获取当前对象的监视器锁,并在 finally 块中调用 Monitor.exit 方法释放该锁。因此,在 synchronized 方法内部的代码可以保证在任意时刻只有一个线程可以访问

常见面试题

  • synchronized 方法和 synchronized 块的区别是什么?
    作用范围:synchronized 方法将整个方法体作为同步区块,而 synchronized 块可以将任意代码块作为同步区块
    锁的对象:synchronized 方法锁定的是整个对象,而 synchronized 块锁定的是在括号内指定的对象
    可控性:synchronized 方法的同步粒度比较大,不够灵活;而 synchronized 块可以更灵活地控制同步代码块的大小
    综上所述,在确定同步粒度时,通常使用 synchronized 块比使用 synchronized 方法更灵活,但是如果整个方法都需要同步,使用 synchronized 方法会更加简单易懂
  • 什么情况下可以使用 synchronized 关键字?
    synchronized 关键字可以用于在多线程环境下保证方法或代码块的原子性。具体来说,如果一个线程正在执行同步方法或代码块,则其他线程将无法访问该方法或代码块
    常见情况包括:
    当多个线程访问共享资源时,可以使用 synchronized 关键字保证线程的安全
    在访问共享变量时,需要对其进行同步控制
  • 在线程通信中,可以使用 synchronized 关键字保证线程之间的同步通信
    synchronized 关键字的性能开销如何?
    synchronized 关键字的使用会带来一些性能开销,因为它需要在多个线程之间进行同步。当线程访问同步代码块时,它必须获得锁,这会增加额外的开销。如果同步代码块执行时间过长,其他线程将一直等待,进而降低程序的性能。
    因此,应该尽量避免在高并发情况下使用 synchronized,或者使用其他的并发控制机制,如 java.util.concurrent 包中的锁和原子操作类等。
  • synchronized 关键字如何实现可重入?
    “可重入” 指的是同一线程可以多次获取同一个锁。例如,当线程 A 进入一个同步块时,如果它再次试图进入该块,则可以再次获取锁,而不会发生死锁
    在 Java 中,synchronized 关键字可以实现可重入,原因如下:
    synchronized 关键字使用对象监视器锁来实现同步。
    对象监视器锁是基于线程的,并且每个线程有一个独立的计数器,用于跟踪它在当前对象上获取的锁的数量。
    当线程试图获取锁时,如果它已经拥有该锁,则计数器将递增。
    当线程退出同步块时,计数器将递减。
    只有当计数器为零时,该线程才会释放锁。
    因此,如果一个线程在同一对象上多次进入同步块,它将多次获得该锁,并在退出该块时多次释放该锁。因此,synchronized 关键字是可重入的。
  • synchronized 关键字与 lock 机制的比较?
    synchronized 关键字和 Lock 机制都是用来保证线程同步的方法。但是它们有一些明显的差异:

灵活性:Lock 机制比 synchronized 关键字更灵活,因为它提供了更多的锁定操作,例如可以实现公平锁和非公平锁,还可以实现读写锁。

可中断性:Lock 机制可以中断一个线程的等待,而 synchronized 关键字不能。

可重入性:synchronized 关键字是自动可重入的,而 Lock 机制必须手动实现。

性能:如果比较的是相同的锁定操作,synchronized 关键字通常比 Lock 机制更快,因为它是内置的。

总体而言,在简单的同步情况下,synchronized 关键字更方便,但是在需要更多灵活性的情况下,Lock 机制可能是一个更好的选择。

总结

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

相关文章

  • SpringBoot执行异步任务Async介绍

    SpringBoot执行异步任务Async介绍

    这篇文章主要为大家介绍了SpringBoot执行异步任务Async示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Springboot实现从controller中跳转到指定前端页面

    Springboot实现从controller中跳转到指定前端页面

    Springboot实现从controller中跳转到指定前端页面方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • SpringBoot中的自定义Banner详细解析

    SpringBoot中的自定义Banner详细解析

    这篇文章主要介绍了SpringBoot中的自定义Banner详细解析,Banner即横幅标语,我们在启动SpringBoot项目时会将Banner信息打印至控制台,我们可以输出一些图形、SpringBoot版本信息等内容,需要的朋友可以参考下
    2024-01-01
  • java编译命令基础知识点

    java编译命令基础知识点

    在本篇文章里小编给大家整理的是一篇关于java编译命令基础知识点内容,有兴趣的朋友们可以学习下。
    2021-01-01
  • 浅谈Java对象禁止使用基本类型

    浅谈Java对象禁止使用基本类型

    本文主要介绍了浅谈Java对象禁止使用基本类型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • JetBrains 发布下一代 IDE无比轻量几秒就能启动干活

    JetBrains 发布下一代 IDE无比轻量几秒就能启动干活

    虽然 JetBrains 公司说 Fleet 的定位和目标并不是代替其他 IDE,但个人觉得, 如果 Fleet 火起来了,其他 IDE 就会黯然失色,特别是多语言开发者,谁愿意装多个 IDE 呢?到时候,可能 JetBrains 以后的所有 IDE 要一统江湖了
    2021-12-12
  • Java之获取客户端真实IP地址的实现

    Java之获取客户端真实IP地址的实现

    在开发工作中,我们常常需要获取客户端的IP,本文主要介绍了Jav之获取客户端真实IP地址的实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • Java数据结构之ArrayList从顺序表到实现

    Java数据结构之ArrayList从顺序表到实现

    Java中的ArrayList是一种基于数组实现的数据结构,支持动态扩容和随机访问元素,可用于实现顺序表等数据结构。ArrayList在内存中连续存储元素,支持快速的随机访问和遍历。通过学习ArrayList的实现原理和使用方法,可以更好地掌握Java中的数据结构和算法
    2023-04-04
  • springboot拦截器过滤token,并返回结果及异常处理操作

    springboot拦截器过滤token,并返回结果及异常处理操作

    这篇文章主要介绍了springboot拦截器过滤token,并返回结果及异常处理操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • SpringBoot整合Retry的详细指南

    SpringBoot整合Retry的详细指南

    在现代的分布式系统中,服务间的调用往往需要处理各种网络异常、超时等问题,重试机制是一种常见的解决策略,本文将通过一个具体的使用场景来详细介绍如何在 Spring Boot 应用中集成和使用 Spring Retry 技术,需要的朋友可以参考下
    2024-12-12

最新评论