解决线程安全问题--volatile

文章讨论了Java中volatile关键字的作用,它如何确保内存可见性,避免多线程中的竞态条件。通过例子展示了volatile如何强制与主内存同步,使线程t1能感知到flag的修改。同时指出volatile并不保证原子性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        为什么要引入volatile关键字? 观察以下这个代码以及他的运行结果.

        

static int flag = 0;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Thread t1 = new Thread(() ->{
            while(flag == 0){

            }
            System.out.println("t1结束!");
        });
        Thread t2 = new Thread(() ->{
            flag = scanner.nextInt();
        });
        t1.start();
        t2.start();
    }

        

        可以观察到,即使我们以及输入了1将flag修改了,但是t1中仍然在执行flag为0的逻辑.

        像这样的问题,成为内存可见性问题。

        这个问题是如何产生的?

        在代码的执行过程中,编译器以及JVM会对我们写的代码进行“优化”,但是这个优化在多线程时可能会出现问题。

        例如,在上面的代码中,flag被修改但是t1没有察觉到,因为t1执行的是:将flag的值读取到工作内存中,在工作内存中比较flag是否等于0.由于t2中等待I/O需要时间,所以在我们输入1之前,t1的工作已经做了非常多次了,在这些非常多次的工作中,JVM发现flag的值没有被修改,且t1的代码中也没有修改flag值的操作,所以他就将操作消耗大的读取操作取消掉了,也就是只进行比较。

此时如果我们输入1,t1也不会去内存中读取被修改掉的flag的值,所以t1中所进行判断的flag依旧是0.

        解决内存可见性问题——volatile

        volatile的作用:

        代码在写⼊volatile修饰的变量的时候,改变工作内存中变量的值,然后将改变后的值刷新到主内存中。

        代码在读取volatile修饰的变量的时候,从主内存中读取volatile变量的最新值到⼯作内存中,然后再读取工作内存中的变量的值。

        当一个变量被volatile修饰后,每次应用到这个变量时,都会强制与主内存同步来保证变量的值正确,这样就能解决上述的问题.

        解决案例:

volatile static int flag = 0;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Thread t1 = new Thread(() ->{
            while(flag == 0){

            }
            System.out.println("t1结束!");
        });
        Thread t2 = new Thread(() ->{
            flag = scanner.nextInt();
        });
        t1.start();
        t2.start();
    }

        

        在输入1之后,t1立刻结束,符合我们的预期。

        注意:volatile不保证原子性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值