volatile简介

volatile

jmm8个原子性操作

  1. lock :将一个变量标记为线程独占;
  2. read :从主内存中读取数据;
  3. load :将读取的数据加载到线程工作内存中;
  4. use :将工作内存的数据给执行引擎执行;
  5. assign :将执行结果赋值给工作内存;
  6. store :将工作内存的数据存回主内存;
  7. write :将线程存回主内存的数据写入原始变量;
  8. unlock :释放锁;

流程图

图片:
总线:就是主内存和cpu数据传输的通道

问题

并发下,数据一致性没法保证。

解决方式一(加锁)

加锁 – 我们看到还有lock和unlock两个操作在流程图上没有体现。
加锁
这是在数据操作的整个流程加锁;
缺点也很明显:数据操作串行化了、效率很低。

解决方式二(MESI缓存一致性协议)

1、cpu有个总线嗅探机制 – 监听;
2、数据store的时候经过总线;
3、cpu会感知到变量已经改变了;
4、cpu将自己工作内存中的该副本变量置为失效 – 失效了再操作的时候就会从新从主内存read。

这样还是会存在问题:多个线程都在进行store怎么办?
例如:两个线程都在store,相继执行了write,而有其他线程在两个线程write中间read;这样读取到的不是最新的数据。

解决:加锁 – 将store和write加锁。保证这两步的原子性

ok…volatile就是帮我们做了这些事
1、开启缓存一致性协议和cup嗅探机制;
2、加锁,保证store和write的原子性。
volatile的加锁属于局部加锁,这样既保证了一致性,又保证了并发性。因为cpu操作很快,两个原子操作同步基本上不会影响性能。

如果volatile修饰后,将我们的代码反汇编后是可以看到assign操作有个lock前缀指令
cup看到lock指令:
1、会将缓存数据立刻写会主内存;
2、volatile是在store前lock;
3、触发MESI协议 – 其他cpu失效自己的数据。

可见性:一个线程修改了数据,另外一个线程可见/感知到。

附带一个volatile经典例子:

//    volatile boolean flag = false;
    boolean flag = false;
    public void volatileTest(){

        new Thread(() -> {
            while (!flag){}
            MyLogger.logger.info("我跳出来了");
        }, "线程一").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {}
            MyLogger.logger.info("我修改状态");
            flag = true;
        }, "线程二").start();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值