0
点赞
收藏
分享

微信扫一扫

【开发实战】彻底给你讲清楚 volatile关键字

1. 什么是 volatile 关键字?

volatile 是 Java 中的一个关键字,用于修饰变量,确保变量的可见性和禁止某些编译器和处理器的优化。volatile 变量的主要作用是确保多线程环境下的变量可见性和有序性。

  1. 可见性:当一个线程修改了 volatile 变量的值,其他线程可以立即看到这个变化。
  2. 有序性volatile 变量的写操作不会被重排序到其前面的读操作之前,读操作也不会被重排序到其后面的写操作之后。

2. volatile 关键字底层原理

volatile 关键字的底层原理主要涉及到内存模型和硬件指令。

内存模型:

Java 内存模型(Java Memory Model, JMM)定义了多线程环境下的内存可见性和有序性规则。volatile 变量的读写操作会直接与主内存进行交互,而不是线程的工作内存。

  1. 写操作

    • 当一个线程写入一个 volatile 变量时,JMM 会确保在此之前对该变量的所有读写操作都已完成,并且结果已经刷新到主内存。
    • 写操作完成后,其他线程可以看到最新的值。
  2. 读操作

    • 当一个线程读取一个 volatile 变量时,JMM 会确保在此之后对该变量的所有读写操作都不会被重排序到此读操作之前。
    • 读操作会从主内存中读取最新的值,而不是线程的工作内存。

硬件指令:

volatile 变量的读写操作通常会生成特定的内存屏障(Memory Barrier)指令,确保内存操作的顺序性。

  1. LoadStore 屏障:在读取 volatile 变量之前插入,确保之前的读操作不会被重排序到此读操作之后。
  2. StoreStore 屏障:在写入 volatile 变量之后插入,确保之后的写操作不会被重排序到此写操作之前。
  3. StoreLoad 屏障:在写入 volatile 变量之后插入,确保之后的读操作不会被重排序到此写操作之前。

3. volatile 的 happens-before 关系

volatile 变量的读写操作建立了 happens-before 关系,确保了内存可见性和有序性。

  1. 写操作 happens-before 读操作

    • 如果一个线程 A 写入一个 volatile 变量 V,然后另一个线程 B 读取同一个 volatile 变量 V,那么线程 A 的写操作 happens-before 线程 B 的读操作。
    • 这意味着线程 A 对 V 的写操作对线程 B 可见,线程 B 读取到的是最新的值。
  2. 传递性

    • 如果 A happens-before B,B happens-before C,那么 A happens-before C。
    • 例如,线程 A 写入 volatile 变量 V,然后写入普通变量 X,线程 B 读取 volatile 变量 V,然后读取普通变量 X,那么线程 A 对 X 的写操作对线程 B 可见。

4. volatile 禁止重排序

volatile 变量的读写操作禁止了某些编译器和处理器的优化,确保了内存操作的顺序性。

  1. 禁止写重排序

    • volatile 变量的写操作不会被重排序到其前面的读操作之前。
    • 例如,a = 1; b = 2; volatileVar = true; 中,a = 1b = 2 不会被重排序到 volatileVar = true 之后。
  2. 禁止读重排序

    • volatile 变量的读操作不会被重排序到其后面的写操作之后。
    • 例如,volatileVar = true; a = 1; b = 2; 中,a = 1b = 2 不会被重排序到 volatileVar = true 之前。

以下是一个使用 volatile 关键字的示例,展示了 volatile 变量的可见性和禁止重排序的特性:

public class VolatileExample {

    private static volatile boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        Thread writer = new Thread(() -> {
            System.out.println("Writer thread started.");
            // 模拟一些耗时操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("Writer thread set flag to true.");
        });

        Thread reader = new Thread(() -> {
            System.out.println("Reader thread started.");
            while (!flag) {
                // 等待 flag 变为 true
            }
            System.out.println("Reader thread detected flag as true.");
        });

        writer.start();
        reader.start();

        writer.join();
        reader.join();
    }
}

在这个示例中,writer 线程在一段时间后将 flag 设置为 truereader 线程会不断检查 flag 的值,直到 flag 变为 true。由于 flagvolatile 变量,reader 线程可以看到 writer 线程对 flag 的最新修改。

volatile 关键字在 Java 中用于确保变量的可见性和禁止某些编译器和处理器的优化。通过建立 happens-before 关系,volatile 变量的读写操作确保了内存可见性和有序性。正确使用 volatile 可以有效解决多线程环境下的数据竞争问题。

举报

相关推荐

0 条评论