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

yizhihongxing

一、什么是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日

相关文章

  • SpringBoot集成内存数据库Sqlite的实践

    下面我将为您详细讲解“SpringBoot集成内存数据库Sqlite的实践”的完整攻略。 1. 关于Sqlite Sqlite是一种轻量级的关系型数据库,最主要的特点是没有独立的进程,所有的数据库操作都直接在应用程序内部完成,这使得Sqlite非常适合一些较小的场景,例如移动应用或单机桌面应用等。由于Sqlite的持久化方式是基于文件的,所以它也被称为嵌入式…

    Java 2023年5月20日
    00
  • Java新手环境搭建 JDK8安装配置教程

    Java新手环境搭建 JDK8安装配置教程 为了学习和开发Java程序,需要安装和配置Java Development Kit(JDK)。在本文中,将介绍如何在Windows操作系统上安装和配置JDK 8,并配置环境变量。 步骤1: 下载JDK8 首先,需要从Oracle官方网站下载适合的JDK8版本。可以从以下链接下载JDK8文件: JDK8官方下载页面 …

    Java 2023年5月24日
    00
  • 史上最全MyBatis面试题及答案

    史上最全MyBatis面试题及答案攻略 什么是MyBatis?它的作用是什么? MyBatis是一个持久层框架,用于简化Java应用程序中的数据库交互。它使用XML或注解来描述对象映射器,从而实现将Java对象映射为数据库表中的数据。MyBatis的主要作用是:简化数据库交互代码的编写,防止SQL注入攻击,提高代码的可维护性和可读性。 MyBatis中的Ma…

    Java 2023年5月20日
    00
  • Java基础-Java基本数据类型

    Java基础-Java基本数据类型 Java中的数据类型分为两类: 基本数据类型和引用数据类型。基本数据类型共8种,分别是byte、short、int、long、float、double、boolean、char。本文将详细介绍Java的基本数据类型。 byte byte类型是最小的数据类型,占1个字节(byte),取值范围是-128到127。当我们需要存储…

    Java 2023年5月26日
    00
  • Java集合功能与用法实例详解

    Java集合功能与用法实例详解 Java集合是Java编程语言中的一种容器,可以存储和操作对象。Java集合提供了一组接口和类,用于快速创建各种不同类型的集合,如列表(List)、集(Set)、图(Map)等。在本文中,我们将详细探讨Java集合的功能和用法,并提供两个实例说明。 Java集合的分类 Java集合被分为以下三个主要类别: List:列表类集合…

    Java 2023年5月26日
    00
  • springboot 自定义启动器的实现

    下面是关于“springboot 自定义启动器的实现”的攻略,包含两个示例: 一、为什么要自定义启动器 Spring Boot是一款非常流行的Java Web框架,它极大地提高了我们的开发效率。而自定义启动器则是在Spring Boot框架下进行自定义的一种方式。通常情况下,我们会将一系列相关的模块封装进一个自定义的启动器中,以便于其他项目能够更加方便的使用…

    Java 2023年5月19日
    00
  • Java SpringBoot核心源码详解

    Java SpringBoot核心源码详解攻略 什么是SpringBoot SpringBoot是基于Spring Framework的快速构建容易维护的Web项目的框架。它的设计理念是提供开箱即用的功能,减少开发者的配置工作。 SpringBoot的核心源码 SpringBoot的启动流程 SpringBoot的启动过程基于Spring Framework…

    Java 2023年5月19日
    00
  • Java实现ATM取款机程序

    下面我将为您详细讲解Java实现ATM取款机程序的完整攻略。整个过程可以分为三部分:1.创建账户;2.登录账户;3.执行取款操作。 1. 创建账户 首先,我们需要定义一个Account类,包括属性:账号、密码、余额等。代码如下: public class Account { private String accountNumber; // 账号 privat…

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