Java synchronized底层实现原理以及锁优化

Java中的synchronized关键字用于保证同步访问,避免出现多线程并发访问共享资源的问题,保证程序的正确性和一致性。在JVM中,synchronized的实现原理是通过Java对象头中的一个有关锁的标识位来实现的,具体的底层实现原理如下:

Java对象头

Java对象在堆中的数据结构是由对象头和实例数据两部分组成的,其中对象头占用了8个或者12个字节(64位JVM为12字节,32位JVM为8字节)。JVM对象头中主要包含了两部分信息:对象的hashCode以及对象的锁信息,锁信息主要有以下内容:

  • Mark Word:存储对象的标志位信息,主要有锁状态及一些标志位;
  • Klass Pointer:指向该对象对应的类元信息指针。

synchronized的实现原理

synchronized 关键字可以用来修饰普通方法、静态方法和代码块,这里主要讲解普通方法的synchronized实现原理。

普通方法的synchronized底层实现主要包含四个步骤:

  1. 当线程A来执行synchronized修饰的方法时,首先会检查该方法的锁状态是否处于 ''Unlocked" 状态;
  2. 如果处于 ''Unlocked" 状态,线程A则会获得该方法所对应对象的锁并将锁的状态设置为 ''Locked";
  3. 如果处于 ''Locked" 状态,线程A就会进入同步队列(等待队列);因为该方法是被synchronized修饰的,只能被一个线程执行,因此如果有多个线程来调用该方法,则会进入同步队列中阻塞等待执行;
  4. 当同步队列中线程的锁状态被释放时,唤醒队列中的线程,然后其中一条线程再次尝试获取该方法所对应对象的锁并将锁的状态设置为 ''Locked",取得锁的线程进入执行状态,其他线程仍然进入同步队列中等待。

锁优化

由于synchronized在实现同步的时候会带来一定的系统开销和调度开销,因此,JVM提供了一些锁优化来提高程序的执行效率,其中包括:

自旋锁

自旋锁是通过循环CAS(Compare-And-Swap)操作实现的,当线程发现自己要获取的锁已经被其他线程获取时,它不会马上进入阻塞状态,而是采用循环的方式不断尝试获取锁(重试),直到获取锁成功或者达到一定重试次数为止。自旋锁适用于锁竞争情况不是很激烈的场景。

锁消除

在程序执行过程中,如果JVM发现某些锁不会被多线程访问,那么JVM就会自动把锁消除掉,从而避免不必要的锁竞争。

例如:

public String getString(String str1, String str2) {
    StringBuffer stringBuffer = new StringBuffer();
    stringBuffer.append(str1).append(str2);
    return stringBuffer.toString();
}

上述代码中,由于StringBuffer对象只会在当前方法中使用,并不会被其他的线程访问,因此JVM可以自动把该方法中的锁消除掉,从而提高方法的执行效率。

轻量级锁

轻量级锁是为了避免线程在执行同步代码块时频繁地进入阻塞状态而引入的一种优化方式。JVM使用CAS操作,把当前线程的锁对象的MarkWord复制到线程的本地变量中,然后再尝试用CAS操作将线程本地变量的MarkWord替换回原对象的MarkWord。

偏向锁

偏向锁是JVM提供的一种轻量级锁优化策略,它适用于哪些只会有一个线程访问的情况。偏向锁的核心思想是:如果一个线程在访问一个锁对象之前,该锁对象没有被其他线程访问过,那么该线程就可以获得该锁,并把MarkWord中的偏向锁标识符设置为该线程的唯一标识符。这样,如果以后再有该线程访问该锁对象,那么该线程就可以直接获取锁,而不再需要竞争。如果其他线程要访问该锁对象,则会先调用CAS操作来检测当前锁标识符是否等于线程的唯一标识符,如果是,则获取锁成功,否则锁标识符被升级为轻量级锁。

例如:

public class Test {
    public static void main(String[] args) {
        synchronized (Test.class) {
            synchronized (Test.class) {
                System.out.println("Nested Lock!");
            }
        }
    }
}

在上述代码中,当线程A第一次执行synchronized块时,会尝试获取Test.class的锁并将该锁的状态设置为 ''Locked",然后线程A对应的锁标识符会被设置为A的特定标识符。当线程A第二次执行synchronized块时,会检测Test.class的锁标识符是否等于线程A的特定标识符,如果等于,则获取锁成功,否则尝试CAS操作将该锁的状态从 ''Unlocked" 修改为 ''Locked";如果修改成功,则锁标识符被设置为A的特定标识符,获取锁成功,否则线程A会将该锁升级为轻量级锁。

综述: 以上就是Java synchronized的底层实现原理以及锁优化的攻略,通过深入了解synchronized关键字的底层实现原理以及锁的优化机制,可以使我们在程序开发过程中更加高效地使用多线程技术,提高程序的性能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java synchronized底层实现原理以及锁优化 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • 基于Java解决华为机试实现密码截取

    我们来详细讲解如何基于Java解决华为机试实现密码截取的问题。 首先,我们需要了解题目的具体要求和背景说明。根据题目描述,我们需要从一个给定的字符串中截取连续的若干个字符,使得这些字符形成的子串在指定的字符串中出现的次数最多。其中,只允许删除掉原字符串中的某些字符,不能增加字符。这个问题可以用动态规划(Dynamic Programming)的思路来解决。 …

    Java 2023年5月19日
    00
  • Java实现链栈的示例代码

    Java链栈是一种特殊的栈,底层是使用单向链表实现的,相比较数组实现栈的方式,链栈可以无需考虑容量的问题,能够动态地适应数据结构的需求。下面详细讲解Java实现链栈的示例代码的完整攻略。 1. 实现链栈的基本步骤 Java实现链栈的基本步骤如下: 定义链栈的节点类 定义链栈类,包含入栈、出栈、查看栈顶数据等方法 在链栈类中,定义一个栈顶节点对象,然后在入栈、…

    Java 2023年5月18日
    00
  • Java的Struts框架报错“TokenNotFoundException”的原因与解决办法

    当使用Java的Struts框架时,可能会遇到“TokenNotFoundException”和“TokenExpiredException”错误。这些错误通常由以下原因之一起: 防止重复提交:Struts框架提供了一种防止重复提交的机制,即使用令牌(Token)来确保每个表单只能提交一次。如果令牌未找到或已过期,则可能会出现这些错误。 配置错误:如果配置文…

    Java 2023年5月5日
    00
  • javaweb登录验证码的实现方法

    下面是“JavaWeb登录验证码的实现方法”的详细攻略: 什么是验证码 验证码(Verification Code)是一种用于判断用户是否为人类的简单程序,主要目的是防止恶意程序对网站进行暴力破解或网络爬虫行为。常见的验证码包括数字、字母、图片、数学公式等形式,验证码输入错误时,通常会跳出提示框要求重新输入。 JavaWeb登录验证码的实现方法 JavaWe…

    Java 2023年6月15日
    00
  • Maven 配置文件 生命周期 常用命令详解

    Maven 配置文件 Maven 是一款基于项目对象模型 (POM) 的构建工具,POM 是 Maven 工作的核心,其中包括了项目依赖、插件配置、构建目标等信息。Maven 配置文件主要分为以下两类: settings.xml settings.xml 文件是 Maven 的全局配置文件,位于 Maven 安装目录的 conf 目录下,主要包括了 Mave…

    Java 2023年5月20日
    00
  • Java线程中sleep和wait的区别详细介绍

    下面详细讲解Java线程中sleep和wait的区别。 sleep和wait的区别 等待方式不同 sleep()方法是线程的静态方法,通过该方法可以使当前线程暂停指定的时间,但不会释放已经持有的锁。wait()方法是Object类中的方法,通过该方法可以使线程等待其它线程通知其被唤醒,同时会释放当前持有的锁。 调用位置不同 sleep()方法可以在任何位置调…

    Java 2023年5月19日
    00
  • 浅析springboot通过面向接口编程对控制反转IOC的理解

    我来为你讲解“浅析Spring Boot通过面向接口编程对控制反转IOC的理解”的完整攻略。 什么是面向接口编程? 面向接口编程是一种开发方式,它将依赖关系从实现类转移到了接口上。实现类不再是主导者,而是被接口所引用。这样可以提高代码的可维护性,降低了类与类之间的耦合度。 什么是控制反转IOC? 控制反转IOC(Inversion of Control)是指…

    Java 2023年5月31日
    00
  • Java实现递归查询树结构的示例代码

    Java实现递归查询树结构的示例代码的攻略包括以下几个步骤: 定义树结构Node类 首先需要定义一个Node类来存储树节点的相关信息,例如节点id、父节点id、节点名称等。Node类的定义如下: public class Node { private String id; // 节点id private String parentId; // 父节点id p…

    Java 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部