原创

java面试题-Java中volatile关键字的作用与解决可见性问题

在Java多线程编程中,保证线程安全性是至关重要的。volatile关键字是一种类型修饰符,用于解决多线程环境下的可见性问题。本教程将深入解析volatile关键字的作用,详细说明其在多线程编程中的应用,以及通过代码演示帮助初学者理解这一概念。

1. 什么是volatile关键字,有什么作用?

volatile关键字用于修饰共享变量,主要解决多线程环境下的可见性问题。在JDK 1.5之后,对volatile关键字的语义进行了增强,它具有以下主要作用:

保证可见性: 确保不同线程对共享变量进行操作时的可见性。当一个线程修改了共享变量的值,修改后的值对其他线程立即可见。

解决有序性问题: 通过禁止编译器、CPU指令重排序和部分happens-before规则,解决有序性问题。

2. volatile可见性的实现原理

在实现可见性时,volatile关键字采用了缓存一致性协议,保证每个线程获得共享变量的最新值。以下是volatile可见性的实现原理:

在生成汇编代码指令时,对volatile修饰的共享变量进行写操作时,会多出Lock前缀的指令。

Lock前缀的指令引起CPU缓存写回内存,导致其他CPU缓存了该内存地址的数据无效。

volatile变量通过缓存一致性协议保证每个线程获得最新值。当CPU发现自己缓存行对应的内存地址被修改,会将当前CPU的缓存行设置成无效状态,重新从内存中把数据读到CPU缓存。

3. 代码演示:解决可见性问题

/**
 * 测试可见性问题
 */
public class TestVisibility {
    // 是否停止 变量
    private static volatile boolean stop = false;

    public static void main(String[] args) throws InterruptedException {
        // 启动线程 1,当 stop 为 true,结束循环
        new Thread(() -> {
            System.out.println("线程 1 正在运行...");
            while (!stop) ;
            System.out.println("线程 1 终止");
        }).start();

        // 休眠 10 毫秒
        Thread.sleep(10);

        // 启动线程 2, 设置 stop = true
        new Thread(() -> {
            System.out.println("线程 2 正在运行...");
            stop = true;
            System.out.println("设置 stop 变量为 true.");
        }).start();
    }
}

在上述代码中,通过将stop变量声明为volatile,确保了在一个线程修改stop值后,对其他线程是可见的。这样,线程1能够及时看到线程2对stop变量的修改,从而正确地结束循环。

程序会一直循环运行下去
这个就是因为 CPU 缓存导致的可见性导致的问题。
线程 2 设置 stop 变量为 true,线程 1 在 CPU 1上执行,读取的 CPU 1 缓存中的 stop 变量仍然为 false,线程 1 一直在循环执行。
file
file

正文到此结束
本文目录