关于volatile提供的可见性无法保证并发编程的思考
题主最近在准备面试,正好复习到了
多线程编程
的知识点。其中有关validate于并发上的使用引起了我的思考。
一、介绍volatile
1. 什么是volatile?
有关volatile
的介绍网上有很多介绍,题主总结能力有限就引用一下百度上的描述:
volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。
2. 可见性
在volatile
的描述中,最常见的一个描述是volatile提供了可见性,可以使不同的线程立刻获取到变量的变化
。至于这点描述主要和JMM(java内存模型)有关,此处博主不在展开描述。
二、原子性
1. 什么原子性
原子性
指的是一条或是一串命令,要么不执行要么全部执行。
2. 通过a++窥见原子性
这里举一个常见的例子:a++
。在代码中a++是一个单独的代码,但是在实际运行的时候大致分为四个步骤。 当然我们假设a的值为1。
1. 从a变量中读出实际的值:1
;
2. 从常量池中出一个值:1
;
3. 将这两个值进行相加算出值:2
;
4. 再将结果赋值到a上:a=2
;
经过这四步后,变量a的值才会变成2
。这四步每一步都是原子操作,但是a++本身不是原子操作。这就说明两个线程在同时执行a++的时候会出现交替执行现象
。
说一个极端情况,两个线程AB同时运行a++,运行的过程是交替的
,即:A.1,B.1,A.2,B.2……我们会发现,最后a的值应该是2,但实际上我们执行了两次a++。
三、可见性无法维持并发安全的原因。
1.结论
通过对原子性和可见性的描述后,某些同学可能已经猜出来为什么可见性无法保证并发安全了
。
可见性可以保证当前获取变量的值为最新状态,但是无法保证后续操作的过程中此值再次改变后能够自动维护成新的值。
2.解释
我们还是按照a++
的例子去说。当我对a变量加上volatile修饰后,可以保证a++操作第一步每次获得值是最新值。但是之后++操作是对读到的值进行操作,与a没有关系了。因此最后赋值操作过程中已经读到的值很可能是过时的
。
四、结论
1. volatile不能保证安全并发的原因
分析问题一定要抓住问题的关键。分析volatile安全并发的问题其关键不在于变量是否具备可见性
,而在于其无法保证操作的原子性,导致一条命令执行中会自动执行其他命令,导致读取到的值变成过时的
。
2. volatile怎么使用
经过上述描述,我们会发现其实volatile无法保证并发是因为无法保证后续操作并不是原子操作,因此配合java提供的锁进行使用。那就没有什么问题了。
后记
博主写完之后发现已经有大佬写出了一篇描述和解释更好的文章,大家可以直接去看大佬的文章。但依然欢迎大家讨论指正~~
Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂)