volatile能保证原子性吗?为什么?

一、问题解析

volatile通常被比喻成”轻量级的synchronized“,也是Java并发编程中比较重要的一个关键字。和synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块等。

volatile的用法比较简单,只需要在声明一个可能被多线程同时访问的变量时,使用volatile修饰就可以了。

但是, volatile在线程安全方面,可以保证有序性和可见性,但是是不能保证原子性的 。

synchronized可以保证原子性 ,因为被synchronized修饰的代码片段,在进入之前加了锁,只要他没执行完,其他线程是无法获得锁执行这段代码片段的,就可以保证他内部的代码可以全部被执行。进而保证原子性。

那么,为什么volatile不能保证原子性呢?因为他不是锁,他没做任何可以保证原子性的处理。当然就不能保证原子性了。
比如有如下代码:

 


public class Test {

    volatile int number = 0;

    public void increase() {
        number++;
    }

    public static void main(String[] args) {
        Test volatileAtomDemo = new Test();
        for (int j = 0; j < 10; j++) {
            new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    volatileAtomDemo.increase();
                }
            }, String.valueOf(j)).start();
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() +
                           " final number result = " + volatileAtomDemo.number);
    }

}

Thread.yield()方法被用来暂停当前正在执行的线程,以允许其他线程获得执行机会,以此来提高并发竞争。 

 

以上程序,正常情况下,输出结果应该是10000,但是真正执行的话就会发现,没办法保证每次执行结果都是10000,这就是因为i++这个操作没办法保证他是原子性的。
i++被拆分成3个指令:
●执行GETFIELD拿到主内存中的原始值number。
●执行IADD进行加1操作。
●执行PUTFIELD把工作内存中的值写回主内存中。

当多个线程并发执行PUTFIELD指令的时候,会出现写回主内存覆盖问题,所以才会导致最终结果不为 10000,所以 volatile 不能保证原子性。

二、粉丝福利

我是浮生,一个工作十四年经验的Java程序员!

最近很多同学问我有没有java学习资料,我根据我从小白到架构师多年的学习经验整理出来了一份80W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴 可以关注我
公众号:“ 
灰灰聊架构 ”, 回复暗号:“ 321 ”即可获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值