Java中Volatile关键字详解及代码示例

一、什么是Volatile?

Volatile是Java中的一种轻量级的同步机制,用于解决多线程并发访问共享变量时的可见性问题,它保证了对变量的修改能够被立即,且正确的读取到。Volatile在Java内存模型中的作用是用来保证线程间数据的可见性。

二、Volatile关键字的使用

  1. 声明Volatile变量

Volatile变量的声明格式为:volatile int x = 0;,即在变量之前加上volatile关键字。在Java中,只有共享变量才需要用到它,如果不是共享变量,使用volatile关键字也不会有任何帮助。

  1. Volatile关键字的特性

① 可见性

如果一个变量被声明为volatile,当一个线程修改了这个变量值时,其他线程能够立即看到最新修改值,而不是使用之前的缓存值。volatile关键字的可见性是通过禁止将数据缓存在寄存器或者其他对线程不可见的地方,以保证多个线程操作主内存中共享变量数据的可见性。

下面是一个示例代码:

public class VolatileVisibilityDemo {

    volatile boolean flag = true;

    public void changeFlag() {
        flag = false;
    }

    public void print() {
        while (flag) {
            System.out.println(Thread.currentThread().getName() + "正在运行...");
        }
    }

    public static void main(String[] args) {
        VolatileVisibilityDemo demo = new VolatileVisibilityDemo();
        new Thread(demo::print, "thread1").start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        demo.changeFlag();
    }
}

在上面的代码示例中,我们首先定义了一个flag变量,并在changeFlag()方法中修改了这个变量的值,然后在print()方法中不断地读取flag变量的值。由于flag变量是volatile类型的,所以当我们修改flag变量的值之后,print()方法中的线程就能立即看到这个最新的修改值,从而退出循环。

如果我们将flag变量修改为非volatile类型的,那么print()方法中的线程可能无法立即看到flag值的最新修改,导致程序在某些情况下无法正确退出循环。

② 禁止指令重排

在Java内存模型中,线程之间的操作是无序的,Java编译器、处理器为了优化程序性能,可能会在不影响单线程执行结果的前提下,对指令进行重排,造成某些结果在多线程的情况下表现出不一致的问题。Volatile关键字可以通过禁止指令重排,来保证线程读写操作的顺序性和可预见性。

下面是一个示例代码:

public class VolatileReorderingDemo {

    private int x = 0;
    private volatile boolean flag = false;

    public void write() {
        x = 5;
        flag = true;
    }

    public void read() {
        if (flag) {
            int y = x + 1;
            System.out.println("y = " + y);
        }
    }

    public static void main(String[] args) {
        VolatileReorderingDemo demo = new VolatileReorderingDemo();
        new Thread(demo::read).start();
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(demo::write).start();
    }
}

在上面的代码示例中,我们先启动一个线程来执行read()方法,然后在1秒之后再启动一个线程来执行write()方法。在write()方法中,我们进行了两个操作:1)修改x的值为5;2)将flag标识设置为true。显然的,由于写操作重排到了读操作之后,如果不加Volatile关键字的话,read()方法读到的x值可能是0,而不是5,从而导致程序的输出结果不符合预期。

通过加入Volatile关键字,我们可以禁止将数据缓存在寄存器或其他对线程不可见的地方,保证多个线程操作主内存中共享变量数据的可见性,并且屏蔽JMM所带来的指令重排优化。这样,就可以保证我们在read()方法中读到的x值是最新的,程序的输出结果也符合预期。

三、总结

Volatile是一个十分重要,使用频率比较高的关键字。它可以保证并发操作的正确性和可靠性,并且相比于传统的synchronized关键字,它具有更轻量级的特性和更高的性能,但也由此带来了一些限制,比如说不支持原子性操作。因此,在使用Volatile关键字的时候,我们一定要清楚自己在做什么,并且结合具体的场景,权衡Volatile关键字的使用效果和影响,才能够真正体现出它的价值。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中Volatile关键字详解及代码示例 - Python技术站

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

相关文章

  • Spring Boot Actuator监控的简单使用方法示例代码详解

    Spring Boot Actuator监控的简单使用方法示例代码详解 Spring Boot Actuator是Spring Boot提供的一个用于监控和管理Spring Boot应用程序的库。它提供了许多有用的端点,可以用于监控应用程序的运行状况、性能和健康状况等。在本文中,我们将详细讲解Spring Boot Actuator的使用方法,并提供两个示例…

    Java 2023年5月15日
    00
  • Java连接数据库,及增删改查的示例

    下面是“Java连接数据库,及增删改查的示例”的完整攻略。 1. 连接数据库 Java连接数据库通常需要使用JDBC API,需要先下载并安装相应的JDBC驱动。一般情况下,不同的数据库使用的JDBC驱动是不同的,我们需要选择对应的JDBC驱动。以MySQL为例,我们可以使用以下步骤来连接数据库: 1.下载MySQL官方提供的JDBC驱动,例如mysql-c…

    Java 2023年5月19日
    00
  • Java初学者常问的问题(推荐)

    Java初学者常问的问题(推荐) 1. Java是什么?为什么要学习Java? Java是一种跨平台的面向对象编程语言,在计算机科学领域中应用广泛。学习Java可以让你掌握面向对象编程的基础概念,这对于日后的编程工作非常有帮助。Java也是许多大型企业和开源项目中常用的编程语言之一,掌握Java可以让你获得更多的就业机会。 2. Java有哪些基础概念? J…

    Java 2023年5月23日
    00
  • java实现登录注册界面

    下面是关于“Java实现登录注册界面”的详细攻略。 需求分析 首先,我们需要分析需求,了解我们需要实现什么样的登录注册功能。一般来说,一个完整的登录注册功能应该包含以下几个部分: 用户注册 用户登录 用户信息管理 数据库操作 技术选型 接下来,我们需要选择适合我们需求的技术栈。这里我们选择Java语言和MySQL数据库。 开发流程 数据库建表 首先,我们需要…

    Java 2023年5月19日
    00
  • javaweb中静态文件的常用处理方法汇总

    本文将全面讲解javaweb中静态文件的常用处理方法,以下是完整攻略。 静态文件处理方法汇总 在javaweb开发中,对于静态文件的处理,主要包括以下几种方法: 1. 直接引用 直接在html页面中引用静态文件,例如: <link rel="stylesheet" type="text/css" href=&qu…

    Java 2023年5月19日
    00
  • 使用java生成json时产生栈溢出错误问题及解决方案

    使用Java生成JSON时如果数据量较大、层次较深,容易出现栈溢出错误。本文将介绍栈溢出的原因及两种解决方案。 问题原因 生成JSON时,Java使用递归方式遍历数据结构,将其转换为JSON格式。如果数据量很大,层次较深,那么递归将产生很多层次的调用,导致栈空间不足,产生栈溢出错误。 解决方案1:调整栈空间大小 Java虚拟机中,栈大小默认为1MB,可通过设…

    Java 2023年5月20日
    00
  • select下拉菜单实现二级联动效果

    要使用select下拉菜单实现二级联动效果,需要以下步骤: 创建HTML结构,包括两个select元素,分别用于显示一级和二级选项,以及相应的label元素。 示例代码: <label for="province">选择省份:</label> <select name="province"…

    Java 2023年6月15日
    00
  • Spring Boot设置并使用缓存的步骤

    让我们来讲解一下“Spring Boot设置并使用缓存的步骤”的完整攻略。 1. 添加缓存依赖 在 pom.xml 文件中添加 spring-boot-starter-cache 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifac…

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