了解 Java 并发编程中的 volatile 关键字

@ 作者: 一恍过去
@ 主页: https://blog.csdn.net/zhuocailing3390
@ 社区: Java技术栈交流
@ 主题: 了解 Java 并发编程中的 volatile 关键字
⏱️ @ 创作时间: 2023年09月08日

了解 Java 并发编程中的 volatile 关键字_第1张图片

目录

  • 前言
  • 1、保证可见性
  • 2、不保证原子性
  • 3、禁止指令重排

前言

volatile 是 Java 中用于修饰变量的关键字,主要用于确保多个线程可以正确地处理被 volatile 修饰的变量。

当多个线程需要访问共享变量时,使用 volatile 可以确保线程之间能够正确地看到最新的变量值,从而避免数据不一致性的问题。

需要注意的是,volatile主要用于标志位和轻量级的状态控制,如果涉及复杂的操作或需要原子性保证的情况,通常需要使用其他并发工具,如 synchronized 块或 java.util.concurrent 包中的类。

1、保证可见性

volatile通过JMM实现数据的可见性。
JMM(Java内存模型): 线程将变量从主内存中拷贝到工作内存,修改完成后将值写到主内存中,并且会被其他线程感知到变量被修改,其他线程将重新从主内存中获取最新值。
主内存中的变量所有线程都能访问。

总结:被volatile修饰的变量能够保证每个线程获取该变量的最新值,从而避免出现数据脏读的现象。

使用说明:
在main中开启5个线程,并且处于while循环状态,如果加上valotile修饰词,2秒后main线程修改num值,其他5个线程则会执行while之后的语句,表示每个线程都感知到了valotile修饰变量值的变化。

代码说明:

public class Test {
    volatile private static int num = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                while (num == 0) {
                }
                System.out.println("线程:" + Thread.currentThread().getName() + "获取最新值");
            }, "test-" + i).start();
        }
        try {
            TimeUnit.SECONDS.sleep(2);
            num = 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

效果:
在这里插入图片描述

2、不保证原子性

volatile不能保证变量的原子性。

比如:n++在字节码中会分为多步执行,因为在并发情况下,多个线程会同时拿到相同的初始化值,并且同时往主内存写入相同的值,所以再累加时会缺失某个数值。

使用说明:
通过运行结果可以看出,最后的value值小于20000,则说明volatile不保证原子性。

代码说明:

public class Test {
    volatile private static int num = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    num++;
                }
            }).start();
        }
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("value:" + num);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

效果:
可能只执行一次value不会小于小于20000,需要多执行几次,比如结果如下:
了解 Java 并发编程中的 volatile 关键字_第2张图片

3、禁止指令重排

执行程序时,为了提高性能,编译器和处理器常常会对指令(字节码)做重排序,分3种类型:
了解 Java 并发编程中的 volatile 关键字_第3张图片
实例代码如下:

int a = 1;  // 1
int b = 2;  // 2
int c = a + b;  // 3

编译器和处理器不会对存在数据依赖关系的操作做重排序。
a和b不存在依赖关系,1、2可以进行重排序;c依赖 a和b,所以3必须在1、2的后面。

在这里插入图片描述

你可能感兴趣的:(java,开发语言,jvm)