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日

相关文章

  • java按字节截取带有汉字的字符串的解法(推荐)

    下面我来详细讲解一下“java按字节截取带有汉字的字符串的解法(推荐)”的完整攻略。该攻略中使用 Java 编程语言来实现。 背景知识 在 Java 中,每个字符都是占用两个字节的,也就是说一个汉字也是占用两个字节的。而按照字节截取一个带有汉字的字符串,我们需要使用字节的方式来进行操作。 解决方案 Java 中提供了一个类 java.nio.charset.…

    Java 2023年5月20日
    00
  • Android性能优化之捕获java crash示例解析

    关于“Android性能优化之捕获java crash示例解析”的完整攻略,我会从以下方面进行详细讲解: 什么是Java Crash? Java Crash是指在Android应用程序中发生了Java异常并导致应用程序崩溃的情况。Java异常是指程序执行过程中出现错误而无法进行正常处理的情况。在应用中,可能会出现各种类型的Java异常,如NullPointe…

    Java 2023年5月27日
    00
  • Nodejs 中文分词常用模块用法分析

    Nodejs 中文分词常用模块用法分析 中文分词一直是自然语言处理领域的重要研究方向,而Nodejs提供了诸多中文分词模块便于使用。本文将详细介绍常用的中文分词模块并给出示例说明。 分词模块介绍 本节将介绍目前比较流行的中文分词模块,包括: nodejieba segment node-segment nodejieba nodejieba是依据结巴分词算法…

    Java 2023年5月19日
    00
  • 什么是Java垃圾收集器?

    什么是Java垃圾收集器? Java垃圾收集器是Java虚拟机(JVM)内存管理的重要组件之一。它负责自动化地释放在程序中不再使用的内存空间。 Java虚拟机的性能直接受垃圾收集器的影响,因为它负责回收内存空间并使可用空间保持在一个良好的状态。 Java垃圾收集器的分类 Java提供了多个垃圾收集器,它们在处理对象分配和回收方面有不同的策略和性能特点。 Ja…

    Java 2023年5月11日
    00
  • 利用Redis实现延时处理的方法实例

    关于如何利用Redis实现延时处理,可以采取以下步骤: 步骤1:安装和配置Redis 首先需要确保Redis服务器已经正确安装在本地或远程服务器上,并正确配置了Redis的相关参数。可以通过以下命令检查Redis服务器是否已安装: redis-cli ping 如果已经安装,会返回“PONG”字样。如果未安装,可以参考官方文档进行安装和配置:https://…

    Java 2023年5月26日
    00
  • Hibernate缓存机制实例代码解析

    Hibernate缓存机制实例代码解析 什么是Hibernate缓存机制? —–(这里需要简要介绍一下Hibernate的缓存机制)—– 一级缓存 —–(这里需要进一步深入介绍一下一级缓存)—– 示例1 // 这里是示例代码 示例1说明 —–(这里需要对示例1进行详细说明,包括代码执行的过程,输出的结果,以及与实现一级缓存的机制…

    Java 2023年6月15日
    00
  • java 语句块的使用详解及实例

    Java语句块的使用详解及实例 在Java中,语句块是一段包含多个语句的代码块,可以在其中定义新的变量和方法,这些变量和方法只在当前语句块内有效。本文将详细讲解Java语句块的使用及实例。 1. 什么是Java语句块? Java语句块是Java程序中的一种结构,用于组织和分类代码,Java中有四种类型的语句块: 普通代码块(即局部代码块):一般用来限定变量的…

    Java 2023年5月20日
    00
  • SpringBoot快速配置数据源的方法

    SpringBoot快速配置数据源的方法 在SpringBoot中,可以非常简单快速地配置数据源,一般使用Spring Boot Starter来简化开发过程。 步骤1:添加依赖 在pom.xml中添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId&gt…

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