必须要学会的JMM与volatile

下面我为你详细讲解必须要学会的JMM与volatile的完整攻略。

JMM介绍

JMM(Java Memory Model)即Java内存模型,用于规范Java程序中线程对共享变量的操作。JMM为Java程序中的线程提供可见性、有序性、原子性等保证,从而提高程序并发性能。

JMM提供的保证

  • 可见性: 一个线程修改了共享变量的值,这个值的变化对其他线程是可见的。
  • 有序性: Java程序中的所有操作都是有序的,JMM通过禁止某些重排序优化来保证有序性。
  • 原子性: 对于某些复合操作,JMM保证这些操作要么全部执行,要么全部不执行。

JMM存在的问题

由于JVM的实现,每个线程都有自己的本地内存,线程之间的共享变量存于主内存中,因此在并发编程中需要考虑多线程之间的内存可见性和顺序性问题。

虽然JMM提供了可见性、顺序性、原子性的保证,但是只能保证某种程度的有序性和可见性,并不能保证所有的交互都按照开发人员预想的方式运行。

volatile关键字介绍

volatile关键字是JMM提供的一种保证内存可见性的手段。定义一个变量为volatile,JVM会避免执行重排序和缓存优化,保证该变量的每次写入都立即同步到主内存中,并且每次读取都从主内存中读取。

需要注意的是,volatile保证的是变量的可见性,保证的是单变量的操作是原子的,但是并不保证多次操作是原子的,因此对于多次操作的复合操作仍需使用锁或Atomic类进行保证。

volatile的用法

在Java中,声明一个变量为volatile需要使用volatile关键字来修饰:

public static volatile int count = 0;

在此例中,修饰的是静态变量count。该声明会告诉JVM将count存储到全局内存中,每个线程都将去电明这个全局变量,而且在修改count时会先更新到全局内存中。

下面是一个使用volatile关键字的示例:

public class VolatileTest {
    public static volatile int value = 0;

    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                while (value == 0) {
                }
                System.out.println("Value has changed,value=" + value);
            }
        }.start();

        // main thread paused for 1s to make sure that the new thread already started
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        value = 1;
        System.out.println("Value is ready to change,value=" + value);
    }
}

在该示例中,启动了一个新线程,并在该线程内部对value进行了轮询,判断value是否被main线程修改,如果修改过则打印修改后的结果。在main线程中,修改了value的值为1,此时新线程会感知到value已经被修改并且会结束轮询并打印修改后的结果。

volatile与JMM的关系

JMM中提供了volatile关键字作为保证可见性的手段。volatile可以在一定程度上解决Java编程中的内存可见性问题。但是需要注意的是,volatile仅能保证单个变量的可见性,如果需要保证复合操作的原子性还需要使用synchronized或者Atomic类。

在Java并发编程中,虽然volatile不能保证一些复合操作的原子性,但是在一些特定的场景下使用volatile仍然是非常有用的,例如状态标志、单例模式等。

以上就是“必须要学会的JMM与volatile”的完整攻略了。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:必须要学会的JMM与volatile - Python技术站

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

相关文章

  • Spring 5.0集成log4j2日志管理的示例代码

    下面详细讲解一下“Spring 5.0集成log4j2日志管理的示例代码”的完整攻略。 一、前置知识 在讲解Spring 5.0集成log4j2日志管理的示例代码前,需要了解一下以下基础知识: log4j2:是一个Java的日志框架,可以帮助Java开发者在应用程序中打印日志; Spring 5.0:是一个Java应用程序框架,可以帮助开发者创建Web应用程…

    Java 2023年5月19日
    00
  • Maven 项目用Assembly打包可执行jar包的方法

    下面是针对 Maven 项目使用 Assembly 插件打包可执行 jar 包的完整攻略,包含了两个示例。 准备工作 首先,确保已经安装 Maven 和 JDK 并配置好环境变量。 接下来,需要在 Maven 项目中添加 Assembly 插件的依赖和配置。 在项目的 pom.xml 文件中添加以下依赖: <dependencies> … &…

    Java 2023年5月20日
    00
  • Java Predicate及Consumer接口函数代码实现解析

    Java中的Predicate和Consumer是两种常用的函数式接口,它们可以让我们编写更为简洁、灵活的代码,特别是在处理集合、流等数据时非常有用。 Predicate Predicate可以理解为谓词或者断言,它接受一个输入参数,返回一个布尔类型的值。通常情况下,我们使用Predicate来过滤集合或者流中的数据。 下面是Predicate接口的定义: …

    Java 2023年5月26日
    00
  • 基于SpringBoot实现上传2种方法工程代码实例

    下面是关于“基于SpringBoot实现上传2种方法工程代码实例”的攻略: 1. 概述 SpringBoot提供了很多方便开发的功能,其中之一就是文件上传。文件上传需要前端页面和后端接口配合实现。前端页面负责UI界面展示和获取用户输入,后端接口负责接收上传的文件并保存在服务器上。 2. 文件上传方法 2.1. 前端表单上传 前端表单上传是指用户在页面上填写表…

    Java 2023年5月20日
    00
  • Spring Security密码解析器PasswordEncoder自定义登录逻辑

    下面是详细讲解“Spring Security密码解析器PasswordEncoder自定义登录逻辑”的完整攻略: 1. 理解PasswordEncoder和其实现类 PasswordEncoder是Spring Security中的一个接口,用于加密和解密用户登录密码,在用户登录过程中用于比对用户输入的密码和数据库中存储的加密后的密码是否一致。 Sprin…

    Java 2023年5月20日
    00
  • Java使用FileInputStream流读取文件示例详解

    一、标题 Java使用FileInputStream流读取文件示例详解 二、概述 在Java编程中,我们常常需要通过读取文件的方式获取文件的内容。Java的输入输出流提供了The FileInputStream类来实现将文件读取到程序中。本攻略将详细讲解如何使用Java的FileInputStream流读取文件,并给出两条示例。 三、示例1(使用FileIn…

    Java 2023年5月20日
    00
  • javaweb 实现文件下载的方法及实例代码

    下面是详细讲解“javaweb 实现文件下载的方法及实例代码”的完整攻略。 前言 文件下载是 Web 开发中一个常见的需求场景,例如在网站中提供软件、文档、图片等资源的下载。本文将介绍如何在 JavaWeb 中实现文件下载功能,并提供两个示例代码。 实现方法 1. 基本思路 要实现文件下载功能,我们需要完成以下两个步骤: 从服务器上读取需要下载的文件,获取文…

    Java 2023年6月2日
    00
  • java抛出异常与finally实例解析

    Java 抛出异常与 finally 实例解析 异常简介 在程序运行过程中,有一些无法预知的情况可能会导致程序出现异常。Java 语言提供了异常机制用于处理程序出现的异常情况。当程序发生异常时,系统会自动抛出一个异常对象,程序可以通过捕捉异常对象并进行处理,从而解决异常情况。 在 Java 中,异常分为两种:运行时异常和受检异常。运行时异常一般指在程序运行过…

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