JAVA内存模型(JMM)详解

JAVA内存模型(JMM)详解

什么是JMM

JMM 是 Java Memory Model 的缩写,即 Java 内存模型,是一种制定了共享内存系统中多线程访问规则的抽象规范。它规定了 JVM 中各个线程之间的共享变量存储在主内存中,每个线程都有自己的工作内存和虚拟机栈,变量值的更改仅在工作内存中进行,需要同步到主内存中才能被其他线程看到。

JMM 可以保证 Java 多线程程序在并发执行时的可见性(Visibility)、有序性(Ordering)和原子性(Atomicity)。

JMM 规定的内存模型

在 JMM 的规范中,分别定义了以下不同的内存区域:

  • 主内存:所有变量的存储位置,是所有线程共享的。
  • 工作内存:每个线程中会包含有一个工作内存,线程的操作即在工作内存中进行。
  • 指令集:CPU 中的指令。
  • CAS 操作:原子操作的一种,即比较并交换。

JMM 同时也规定了以下行为:

  • 线程间通信(Lock,volatile 变量,synchronized,AtomicInteger 等):保证可见性、有序性和原子性。
  • happens-before 关系:保证程序运行的顺序。
  • 内存屏障(Memory Barriers):强制工作内存和主内存之间的通信。

JMM 的实例

以下是两个使用 JMM 的实例:

示例1:内存可见性

下面是一个简单的多线程示例,其中线程 A 在运行时修改了共享变量 count 的值,而线程 B 需要使用这个 count 值。如果不使用 JMM,线程 B 很大概率无法读取到线程 A 修改后的值。

public class VisibilityDemo {
    private volatile static int count = 0;

    public static void main(String[] args) {
        new Thread(() -> {
            while (count == 0) {}
            System.out.println(Thread.currentThread().getName() + " exit");
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        count = 1;
        System.out.println(Thread.currentThread().getName() + " set count to 1");
    }
}

使用 volatile 关键字可以让程序使用 JMM 模型:

private volatile static int count = 0;

输出结果为:

Thread-0 exit
main set count to 1

结论:使用 volatile 可以防止写操作前后的指令重排,保障了内存可见性。

示例2:原子性

明显的例子是多线程下的计数器,如果不是原子性的自增会导致不准确的结果,甚至程序奔溃。使用 JMM 的 AtomicInteger 可以将其变得更安全。

public class AtomicDemo{
    static AtomicInteger ai = new AtomicInteger(1);

    public static void main(String[] args){
        new Thread(() ->{
            int i = ai.getAndSet(2);
            System.out.println("Thread1: ai = " + ai + ", i = " + i);
        }).start();

        new Thread(() ->{
            int i = ai.getAndIncrement();
            System.out.println("Thread2: ai = " + ai + ", i = " + i);
        }).start();
    }
}

输出结果:

Thread1: ai = 2, i = 1
Thread2: ai = 3, i = 2

结论:使用 AtomicInteger 可以保证一个自增操作的原子性。

结论

JMM 的核心思想是通过将共享变量存储在主内存中,为每个线程创建一个工作内存,严格限制对共享变量的访问,保证并发环境下多线程的安全性。JMM 的实现方式和规范可以保证多线程程序的可见性、有序性和原子性要求,是 Java 多线程程序实现的重要基础。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JAVA内存模型(JMM)详解 - Python技术站

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

相关文章

  • Java 超基础讲解String的使用

    我将为您提供 Java 超基础讲解 String 的使用的完整攻略,具体如下: 一、什么是 String 在 Java 中,String 是一个类,它代表了一个字符串。 二、创建一个字符串变量 可以通过两种方式来创建一个字符串变量: 1. 直接赋值 String str1 = "Hello World"; 2. 使用构造方法 String…

    Java 2023年5月26日
    00
  • Java中统计字符个数以及反序非相同字符的方法详解

    Java中统计字符个数的方法详解 在Java中可以使用几种方法来统计字符串中字符的个数,下面介绍一些常用的方法。 1.使用for循环 可以使用for循环遍历字符串,逐个判断字符是否相同或满足某些条件,从而统计字符个数。 示例代码: public int countChar(String str, char c) { int count = 0; for (i…

    Java 2023年5月27日
    00
  • Android仿eleme点餐页面二级联动列表

    下面是Android仿eleme点餐页面二级联动列表的攻略: 1. 简介 eleme是一款非常流行的外卖APP,其点餐页面上的二级联动列表的效果颇为优秀。仿eleme点餐页面二级联动列表就是模仿eleme点餐页面的效果,实现类似的二级联动效果。 2. 实现过程 实现仿eleme点餐页面二级联动列表的过程主要分为以下几个步骤: 2.1. 数据准备 比较一下el…

    Java 2023年5月23日
    00
  • Java流程控制语句最全汇总(上篇)

    《Java流程控制语句最全汇总(上篇)》是一篇详细介绍Java中流程控制语句的文章,包含了if语句、switch语句、while循环、do-while循环、for循环、break语句、continue语句等内容。以下是该篇文章的详细攻略: 一. if语句 在Java中,if语句用于判断某个条件是否成立,并根据判断结果执行相应的代码块。if语句的基本语法如下:…

    Java 2023年5月19日
    00
  • java实现遍历树形菜单两种实现代码分享

    下面我将详细讲解Java实现遍历树形菜单的两种实现代码分享,包括以下内容: 遍历算法的概念 遍历树形菜单的两种实现方式 示例代码和详细解释 一、什么是遍历算法? 在讲解树形菜单的遍历算法之前,我们先来了解一下遍历算法的概念。 遍历算法是对数据结构中所有元素进行无遗漏且不重复的访问,以达到数据处理的目标。 在树形菜单的遍历中,我们需要访问每一个节点,以获取每个…

    Java 2023年5月20日
    00
  • java实现日历(某年的日历,某月的日历)用户完全自定义

    实现Java日历需要用到以下几个方面的知识: 日期和时间类:Java中有java.util.Date和java.util.Calendar类,但这些类已经被Java 8中的全新日期和时间API(Java.time)所取代,建议使用新API。 控制台输出:Java中有System.out.println()方法可以将内容输出到控制台。 用户输入:Java中有S…

    Java 2023年5月20日
    00
  • java数据结构基础:绪论

    Java数据结构基础:绪论 什么是数据结构 数据结构是指为了有效地组织和存储数据,以便于访问和修改,而设计出来的抽象数据类型和实现方法。简单的来说,就是数据之间的关系组织形式。 为什么要学习数据结构 数据结构是计算机科学的核心领域之一,了解和掌握数据结构对于程序设计和编写具有重要的意义: 合理选择数据结构,可以使程序更加高效地执行和管理数据。 理解数据结构的…

    Java 2023年5月30日
    00
  • java返回json请求中文变成问号的问题及解决

    下面是详细讲解“Java返回JSON请求中文变成问号的问题及解决”的完整攻略: 问题描述 在使用Java后端向前端返回JSON格式数据时,如果数据中包含中文字符,有时候会出现中文字符被转换成问号的情况,造成数据不可读。这个问题通常出现在字符编码设置不正确的情况下。 解决方法 方法一:设置字符编码 设置正确的字符编码可以解决这个问题。在Java中设置字符编码有…

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