volatile的用途

前言

在提到多线程高并发时volatile是绕不过去的一个关键字,曾经对volatile也是一知半解,在踩过一些坑之后对volatile有了更深入的理解,在此对volatile做一个总结。

主要用途

  • 变量可见性
    说到多线程环境下变量的可见性问题,首先得提一下Java的内存模型JMM


    JMM

    在JMM模型中内存分为主内存和工作内存,每个线程拥有自己独立的工作内存,主内存、工作内存互相之间不影响。比如说我们在主线程中定义了一个变量a,那么变量a是存储在主内存中的,其它线程会拷贝一份变量a的副本存储在各自的工作内存中,当修改了主线程中变量a的值时其它线程是感知不到的,那么此时volatile就派上用场了。当给变量a添加volatile修饰之后,变量a的值发生变化时其它线程就会感知到。下面通过举个例子来说明这一点:

public class VolatileTest {
    public static Boolean flag = false;

    public static void main(String args[]) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!flag) {
                System.out.println("flag is false");
            }
            if (flag == true) {
                System.out.println("now flag is true");
            }
        });
        t.start();
        Thread.sleep(1000);
        flag = true;
        t.join();
    }
}

当运行这个程序时大家猜猜看结果会是什么呢?会打印出"now flag is true"吗?不会打印这句话,运行结果是程序会陷入死循环中不断打印"flag is false",明明在主线程中设置了flag变量为true,为什么在线程t中flag还是false呢?这就是上面讲的主内存和工作内存中的变量是互不影响的,也就是主线程中的flag变量值被修改为true时,线程t中的flag变量不会被修改依然是false。如果给flag变量增加volatile修饰那程序还会死循环吗?答案是不会死循环了。

public class VolatileTest {
    public volatile static Boolean flag = false; // 添加volatile将flag变量设置为其它线程可见

    public static void main(String args[]) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!flag) {
                System.out.println("flag is false");
            }
            if (flag == true) {
                System.out.println("now flag is true");
            }
        });
        t.start();
        Thread.sleep(1000);
        flag = true;
        t.join();
    }
}

主线程中的flag变量增加volatile修饰之后,线程t能够感应到flag变量值的变化,当主线程中将flag值修改为true时,在线程t中flag值也被修改为true。

  • 防止指令重排
    什么是指令重排呢?简单来讲就是程序中几条语句的执行顺序被打乱了
int a = 1; // 语句1
int b = 2; // 语句2
int c = a * a; // 语句3
int d = b + b; // 语句4

比如这4条语句在单线程场景下执行顺序是1->2->3->4,但是到了多线程场景就不一定是按照1234这个顺序执行了,执行顺序可能是1->3->2->4、2->1->3->4,但不会出现的场景是语句3在语句1之前执行、语句4在语句2之前执行,这是因为语句3对语句1有依赖(同样的语句4对语句2有依赖)。如果想阻止乱序执行语句这种行为可以给变量添加volatile修饰,比如给变量a、b都添加上volatile修饰,那么即使是在多线程场景这4条语句一定会顺序执行。不过要说明一下的是这种乱序执行发生的概率还是比较低的,一旦发生的话程序可能会发生不可预期的结果。我曾经遇到这样一起事件,事后经过反复压测好多次才复现出一次,我当时的做法是在每条语句前后都加上日志,日志中会打印时间戳,通过分析语句执行的时间点才看出来是语句执行乱序了,最后加上volatile之后就没再出现过这种情况了。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容