java线程中共享数据的可见性(Volatile关键字)

1、背景

小皮和小芳为了日后的结婚准备每天都有往结婚基金里面存钱,最近终于是存够了10万,不过他俩都没有每天去银行查账的习惯。有一天,小皮为了玩游戏,从结婚基金里面提取了一万块钱进行充值,但是小芳不知道,她一直以为结婚基金里面有10万块钱……

2、啥都别说先贴简单的示例代码

Money类:

public class Money {
    public static int money = 100000;
}

Test类:
(为了方便我就使用lambda表达式+匿名内部类创建线程了)

public class Test {
    public static void main(String[] args) {

        new Thread(()->{
            System.out.println("XiaoPiThread:Money.money:"+ Money.money);

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Money.money = 90000;
            System.out.println("XiaoPiThread:Money.money:"+Money.money);
        }).start();

        new Thread(()->{
            System.out.println("XiaoFangThread:Money.money:"+Money.money);
            while(Money.money == 100000){
                ///System.out.println("XiaoFangThread:Money.money:"+Money.money);
            }
            System.out.println("结婚基金已经不是十万了!");
        }).start();
    }
}

首先我们来预想一下执行结果,正常情况下是不是应该在小皮拿走10000以后,小芳这边一直在循环,总会知道Money.money最后的值会变成90000,然后输出"结婚基金已经不是十万了!"语句。这是我们预想的正常的结果,那么我们来看一下实际的运行结果:

XiaoPiThread:Money.money:100000
XiaoFangThread:Money.money:100000
XiaoPiThread:Money.money:90000

事实上,根据执行结果来看,只有小皮自己知道结婚基金现在最新的值是90000,但小芳却一直以为是100000,所以一直在死循环,导致没有输出我们预想的结果。但是为什么会这样呢?大家都是拿的Money.money这个变量,按理说小皮线程在修改了这个变量以后,小芳线程总会拿到cpu执行权然后循环知道Money.money已经是90000了,为什么没有输出我们想要的结果呢?其实,这个和线程的栈内存有关。

在JMM的定义里面,堆内存是唯一的,但是栈内存不一定。每一个线程都有自己的线程栈,当线程每次使用堆内存里面的变量时,会优先拷贝一份到变量的副本中,然后通过这个变量副本进行操作。

所以当小皮线程拿到cpu执行权,先睡眠,这时候小芳就会抢到cpu然后拿到堆内存中的Money.money==100000,接着开始循环,在小芳循环的过程中,cpu可能随时被小皮线程抢走,然后执行Money.money = 90000,把小皮线程中的变量副本Money.money更改成90000,当小皮线程执行完毕后,线程会把变量副本的值赋值回堆内存中的变量,此时堆内存中的Money.money已经是100000了,但是小芳线程却一直用的自己线程栈里面的变量副本,Money.money一直是100000,所以她一直在死循环,线程也一直没有结束。

这也是这段代码存在的问题:当A线程修改了共享数据时,B线程没有及时获取到最新的值,如果还在使用原先的值,就会出现问题。

那我们怎么去解决这个问题呢?我们可以通过Volatile关键字来解决。

Volatile关键字 : 强制线程每次在使用的时候,都会看一下共享区域最新的值;

这段代码里的结婚基金是共享数据,我们希望这个数据在每次读写的时候,都是最新的,所以我们要改一下Money类:

public class Money {
    public static volatile int money = 100000;
}

我们在共享变量前面加上Volatile关键字,这样线程每次在使用这个变量时,都会去看一下堆内存里最新的值。

输出结果:

XiaoPiThread:Money.money:100000
XiaoFangThread:Money.money:100000
XiaoPiThread:Money.money:90000
结婚基金已经不是十万了!

从结果可以看出,小芳线程已经知道了结婚基金的最新值不是100000了,最终输出结果结束进程。这样就符合我们的预想结果了。

下一章我们来探讨一下,除了Volatile关键字,还有什么办法可以解决这种数据不统一的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值