【Java笔记】volatile 关键字

一、代码案例

创建两个线程,第一个线程不停的循环去执行自己的任务,第二个线程输入一个停止标识使线程 1 退出

 public class Demo07_volatile {
    // 停止标识
    static int flag = 0;

    public static void main(String[] args) {
        // 创建执行任务的线程
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "线程启动");
            while (flag == 0) {
                // 循环处理的任务
            }
            System.out.println(Thread.currentThread().getName() + "线程退出");
        }, "t1");

        // 启动线程 t1
        t1.start();

        // 创建线程 t2
        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "线程启动");
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个非 0 的整数");
            flag = scanner.nextInt();
            System.out.println(Thread.currentThread().getName() + "线程退出");
        }, "t2");

        // 启动线程 t2
        t2.start();
    }
}

正常来说,如果输入了一个非0整数,线程1会停止,但是事实并非如此,这个代码执行的结果是:

在这里插入图片描述
线程 t1 并没有退出。

二、问题分析(重点)

见下图,线程 t1 将主内存中的 flag 值读取到自己的工作内存(寄存器)中,此时,另外一个线程 t2 也将主内存中的 flag 值读取到自己的工作内存中,但是 t2 将 flag 值修改为了 1 ,重新写回了主内存中,但是对于线程 t1 来说,只是比较 flag 这个变量的值,从来没有修改过,所以 CPU 认为这个值永远不会改变,也不会从主内存重新读取值。这就出现了多线程环境下,一个线程修改了变量而另外一个线程无法感知到的情况,此时程序就会出现bug。因此,想要解决这个问题,最重要的就是当一个线程修改了另一个线程需要的变量,必须要让另一个线程感知到,也就是解决内存可见性问题。

在这里插入图片描述
但在 flag 变量前添加 volatile 关键字之后,线程 t2 修改 flag 值之后,线程 t1 关闭。

在这里插入图片描述

三、原因(Java层面)

加了 volatile 关键字的变量,前后都会加内存屏障,涉及到的读和写都是从主内存中获取,最终解决了内存可见性的问题。内存屏障的作用是保证指令执行的先后顺序,从而保证内存可见性。

volatile 写:
在这里插入图片描述

volatile 读:
在这里插入图片描述

四:总结(记住)

volatile 关键字从真正意义上解决了内存可见性的问题,用 volatile 修饰的变量,由于前后都有内存屏障,保证了指令的执行顺序,与 synchronized 不同,synchronized是通过原子性进而保证了内存可见性,也可以通过禁止指令重排序的方式解决有序性的问题,但是 volatile 不保证原子性。
一句话,volatile 解决了内存可见性,解决了有序性,不保证原子性
多个线程之间涉及的共享变量,如过存在修改的逻辑,只管加 volatile !!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值