详解Java对象结构与对象锁的升级

详解Java对象结构与对象锁的升级

Java对象结构

Java对象在内存中的实际存储由三部分组成:对象头、实例数据和对齐填充。

对象头

对象头是Java对象的一部分,用于存储对象自己的运行时数据,包括以下内容:

  • Mark Word: 用来锁定对象、记录对象哈希值、记录对象所属的分代年龄等信息。
  • Class: 指向对象的Class对象。

在Java 8中,对象头的大小为12字节。

实例数据

实例数据存储对象的实例变量,实例数据相对于对象头的大小是不定的,因为它取决于对象的实例变量数量和类型。

对齐填充

为了对齐对象在内存中的地址,可能需要在实例数据末尾补充填充数据,使得对象的大小是8字节的整数倍。在Java 8中,所需的填充量最多为7个字节。

对象锁的升级

Java中的对象锁分为偏向锁、轻量级锁和重量级锁。

偏向锁

偏向锁是一种针对加锁频繁性不高的场景的优化,偏向锁的意思是,偏向某个线程,这个线程在获取锁之后,当再次进入同步块时,因为上一次已经获取过锁了,所以可以直接获得锁,而不需要重新去竞争。

下面是一个偏向锁示例:

public class BiasedLockDemo {
    private static final int COUNT = 10_000_000;
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        long start = System.currentTimeMillis();
        for (int i = 0; i < COUNT; i++) {
            synchronized (obj) {
                // do nothing
            }
        }
        System.out.println("Synchronized cost : " + (System.currentTimeMillis() - start) + "ms");

        Thread.sleep(10000);

        start = System.currentTimeMillis();
        for (int i = 0; i < COUNT; i++) {
            synchronized (obj) {
                // do nothing
            }
        }
        System.out.println("Synchronized cost : " + (System.currentTimeMillis() - start) + "ms");
    }
}

在上面的示例中,第一次进入同步块时,JVM会偏向此线程,并记录对象的Mark Word为偏向锁模式。因为只有一个线程,所以不会有竞争,性能最优。第二次进入同步块时,因为对象已经记录了偏向线程,所以直接判断Mark Word是否指向该线程,如果是,则直接获取锁,不再竞争锁。

轻量级锁

如果一个线程没有成功获取偏向锁,或者偏向锁被取消了,那么它就会尝试轻量级锁来获取锁。轻量级锁使用CAS操作,将对象头中的Mark Word复制到线程的栈帧中,表示当前线程已经获得了这个锁。如果CAS操作成功,则当前线程成功获得锁,继续执行同步块。如果CAS操作失败,表示有竞争,此时当前线程会自旋一段时间,等待其他线程释放锁。

下面是一个轻量级锁示例:

public class LightweightLockDemo {
    private static final int COUNT = 10_000_000;
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                synchronized (obj) {
                    // do nothing
                }
            }
        }, "thread-1");
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < COUNT; i++) {
                synchronized (obj) {
                    // do nothing
                }
            }
        }, "thread-2");

        long start = System.currentTimeMillis();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Synchronized cost : " + (System.currentTimeMillis() - start) + "ms");
    }
}

在上面的示例中,两个线程同时竞争同一个对象锁,因此会导致竞争,此时第二个线程会自旋等待第一个线程释放锁。

重量级锁

如果轻量级锁获取失败,则会进入重量级锁模式。此时线程会进入操作系统的内核态,进入阻塞状态,等待其他线程释放锁。

下面是一个重量级锁示例:

public class HeavyweightLockDemo {
    private static final int COUNT = 10_000_000;
    public static void main(String[] args) {
        Object obj = new Object();
        synchronized (obj) {
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上面的示例中,线程进入同步块后,会一直等待100秒,此时其他线程无法获取锁,只能等待该线程释放锁,因此此时已经进入重量级锁模式。

总结

Java对象在内存中的实际存储由三部分组成:对象头、实例数据和对齐填充。对象锁分为偏向锁、轻量级锁和重量级锁。在多线程并发环境下,不同的锁模式可以优化性能,提升程序的吞吐量。

以上就是Java对象结构与对象锁升级的详细攻略,希望能够对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java对象结构与对象锁的升级 - Python技术站

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

相关文章

  • SpringMVC @RequestMapping注解作用详解

    以下是关于“SpringMVC @RequestMapping注解作用详解”的完整攻略,其中包含两个示例。 SpringMVC @RequestMapping注解作用详解 在SpringMVC中,@RequestMapping注解是一个非常重要的注解,用于将HTTP请求映射到控制器的处理方法上。本文将详细介绍@RequestMapping注解的作用和用法。 …

    Java 2023年5月16日
    00
  • 浅析Java中对象的创建与对象的数据类型转换

    这里是“浅析Java中对象的创建与对象的数据类型转换”的攻略。 1. 对象的创建 Java中的对象可以由new关键字创建,一个对象的创建需要以下步骤: 分配对象的内存空间:在堆内存中为新对象分配一片连续的空间,这个空间的大小由对象的数据类型和属性决定。 执行构造函数:在分配好内存空间之后,JVM会执行对象的构造函数,初始化对象的属性值等。 将对象的引用返回给…

    Java 2023年5月26日
    00
  • 一篇文章带你入门Java变量及整形

    一篇文章带你入门Java变量及整形 什么是变量? 变量就是在程序执行期间可以发生变化的量。Java是一种强类型语言,声明变量时需要指定变量类型。 声明变量 在Java中声明变量时,需要指定变量的类型,语法为: type name; 其中,type表示变量类型,name表示变量名。例如,声明一个整型变量age: int age; 表示声明一个名为age的整型变…

    Java 2023年5月23日
    00
  • Spring 异常单元测试的解决

    对于“Spring 异常单元测试的解决”的完整攻略,可以分为以下几个步骤来进行讲解。 步骤一:添加必要的依赖 在开始进行 Spring 异常单元测试前,需要在项目文件中添加必要的依赖。这里我们需要添加 JUnit 和 Spring Test 的依赖。 <dependency> <groupId>org.junit.jupiter&lt…

    Java 2023年5月27日
    00
  • Java实现作业调度的示例代码

    下面是Java实现作业调度的示例代码的攻略: 1. 理解作业调度 在计算机系统中,作业是指用户提交的一项任务。作业调度是指操作系统针对用户提交的作业对其进行管理和调度,使其能够高效地执行。作业调度是一个重要的操作系统功能,可以根据用户的需求和系统资源的使用情况,对作业进行优先级、并发度、执行方式等的调度。 2. 利用Java实现作业调度 Java语言具有很好…

    Java 2023年5月18日
    00
  • SpringBoot集成FastDFS+Nginx整合基于Token的防盗链的方法

    下面是 SpringBoot 集成 FastDFS+Nginx 整合基于 Token 的防盗链的方法的完整攻略: 简介 FastDFS 是一个开源的分布式文件系统,由阿里巴巴的余庆编写,目前由开源社区进行开发,FastDFS 是基于 Linux 的文件系统,实现了一个简单的文件系统,它是以 Tracker Server 和 Storage Server 为两…

    Java 2023年5月20日
    00
  • 基于spring boot 的配置参考大全(推荐)

    下面就来详细讲解一下“基于Spring Boot的配置参考大全(推荐)”的完整攻略。 1. 基本介绍 “基于Spring Boot的配置参考大全(推荐)”是一篇非常全面的配置攻略,旨在帮助Spring Boot开发者更好地了解和掌握Spring Boot的配置方式。该文件包含了以下内容: Spring Boot配置文件的基本语法和命名规则 常用的配置方式,包…

    Java 2023年5月15日
    00
  • jsp分页显示完整实例

    下面就来详细讲解一下“jsp分页显示完整实例”的攻略。 什么是分页显示? 分页显示是指将大量数据分开展示,并通过页面的控件使用户可以翻页操作,进行查看。 为什么需要分页显示? 如果展示的数据量过大,会导致页面加载速度变慢,甚至使页面崩溃。另外,用户在查看数据时,如果没有分页功能,会使得他们无法方便地找到所需数据。 实现分页显示的方法 实现分页显示有多种方法,…

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