关于“volatile关键字无法保证原子性”的一些理解

Volatile关键字的粗浅理解

在学习并发编程的时候了解到,volatile关键字有两个作用:

1. 并发环境可见性:volatile修饰后的变量能够保证该变量在线程间的可见性,线程进行数据的读写操作时将绕开工作内存(CPU缓存)而直接跟主内存进行数据交互,即线程进行读操作时直接从主内存中读取,写操作时直接将修改后端变量刷新到主内存中,这样就能保证其他线程访问到的数据是最新数据

2. 并发环境有序性:通过对volatile变量采取内存屏障(Memory barrier)的方式来防止编译重排序和CPU指令重排序,具体方式是通过在操作volatile变量的指令前后加入内存屏障,来实现happens-before关系,保证在多线程环境下的数据交互不会出现紊乱

Volatile无法保证原子性

由于volatile关键字的可见性,导致容易被误解其作用,以下描述是不准确的:“volatile变量对所有线程是立即可见的,对volatile变量所有的写操作都能立即反馈到其他线程中,volatile变量在各个线程中都是一致的,所以基于volatile变量的运算在并发下安全的”

使用volatile关键字虽然能够使线程共享的变量在并发情况下完全可见,起到线程信息交互和通信的作用,但对于非原子操作,volatile并不能保证该操作的原子性(即操作过程被其他线程干扰导致信息错误和信息丢失),最简单的例子就是i++这样的自增操作,以下是一个并发情况下的自增例子:

    private static volatile int count = 0;

    public static void main(String[] args){
        Thread[] threads = new Thread[5];
        for(int i = 0; i<5; i++){
            threads[i] = new Thread(()->{
                try{
                    for(int j = 0; j<10; j++){
                        System.out.println(++count);
                        Thread.sleep(500);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }
    }

生成5条线程,每条线程都对count执行10次自增操作,按理说最后的结果应该是1到50均被打印出来,但不管运行多少次,都无法得到期望的结果,说明volatile标记的变量在并发环境下并不能保证线程安全。

诸如“i++”或者“++i”这样的操作并不是原子操作,因为自增操作包括三个基本指令:读取数据、计算数据、输出结果,可以看看i++相关的字节码:

Code:
       0: getstatic     #2                  // Field count:I
       3: iconst_1
       4: iadd
       5: putstatic     #2                  // Field count:I
       8: return

getstatic指令将变量从主内存中读取出来,这时候如果该变量时volatile修饰的,那可以完全保证此时取到的是最新信息,但在iconst_1指令(入栈)和iadd指令(自增计算)执行过程中,该变量有可能正在被其他线程修改,最后计算出来的结果照样存在问题,因此volatile并不能保证非原子操作的原子性,仅在单次读或者单次写这样的原子操作中,volatile能够实现线程安全

Volatile结合CAS实现原子性

CAS(CompareAndSwap)比较交换原则结合volatile,就能够实现基本的线程安全,典型的应用就是concurrent包下的Atomic类,上述例子如果用AtomicInteger代替int,就能够实现自增情况下的线程安全



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值