并发过程中volatile能否保证线程安全( 二 )


我们来看一个典型的例子 , 伪代码如下 。 执行完所有线程任务 , 我们期望的结果会是30*10000 。 但实际却是一个小于30*10000的数 , 刚开始看到一定觉得有点奇怪 , 但仔细一想就清楚了 。 count++;编译后最终并非一个原子操作 , 它由几个指令一起组合实现 。 在Java内存模型中 , count++;被分割成多个步骤 , 这几步不具有原子性 。 假如在完成的过程中 , 其他线程就去读了主存的count变量 , 那明显将导致脏读现象 。
并发过程中volatile能否保证线程安全文章插图
例子
volatile无锁导致这个问题的原因其实是因为volatile不具备锁操作 , 要解决此问题其实不难 , 就是将这这些操作变为原子操作 。 即保证线程一完成之前不能有其他线程读取count变量 , 要达到目的只需对count变量加一个互斥锁即可 。 线程一执行前对count加锁 , 其他线程无法对count进行访问 。 线程一执行完后释放锁 , 此刻开始才允许其他线程获取此变量 。
总结Volatile是一个很容易搞混的关键词 , 很多经验丰富的开发人员都不能正确使用它 。 本节从机器结构讲到对应的Java内存模型 , 再引出主存与工作内存之间数据同步的问题 。 进而更好地解释了volatile的确切含义 , 它只保证可见性 , 它不足以保证数据的同步性 。
volatile、可见性的底层实现是通过内存屏障实现的 。 内存屏障的另一个作用就是强制刷出各种CPU缓存数据 , 因此任何CPU上的线程都可以读取到这些数据的最新版本 。
更多Java并发原理可关注作者下面的专栏:
作者简介:笔名seaboat , 擅长人工智能、计算机科学、数学原理、基础算法 。 出版书籍:图解数据结构与算法、Tomcat内核设计剖析、图解Java并发原理、人工智能原理科普 。


推荐阅读