写点什么

volatile 为什么不保证原子性

作者:悟空聊架构
  • 2021 年 12 月 12 日
  • 本文字数:924 字

    阅读完需:约 3 分钟

能演示下 volatile 为什么不保证原子性吗?

原子性:一个操作或一系列操作是不可分割的,要么同时成功,要么同时失败。


这个定义和 volatile 啥关系呀,完全不能理解呀?Show me the code!


考虑一下这种场景:


当 20 个线程同时给 number 自增 1,执行 1000 次以后,number 的值为多少呢?


在单线程的场景,答案是 20000,如果是多线程的场景下呢?答案是可能是 20000,但很多情况下都是小于 20000。


示例代码:


package com.jackson0714.passjava.threads;
/** 演示volatile 不保证原子性 * @create: 2020-08-13 09:53 */
public class VolatileAtomicity { public static volatile int number = 0;
public static void increase() { number++; }
public static void main(String[] args) {
for (int i = 0; i < 50; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { increase(); } }, String.valueOf(i)).start(); }
// 当所有累加线程都结束 while(Thread.activeCount() > 2) { Thread.yield(); }
System.out.println(number); }}
复制代码


执行结果:第一次 19144,第二次 20000,第三次 19378。





我们来分析一下 increase()方法,通过反编译工具 javap 得到如下汇编代码:


  public static void increase();    Code:       0: getstatic     #2                  // Field number:I       3: iconst_1       4: iadd       5: putstatic     #2                  // Field number:I       8: return
复制代码


number++其实执行了3条指令


getstatic:拿 number 的原始值 iadd:进行加 1 操作 putfield:把加 1 后的值写回


执行了 getstatic 指令 number 的值取到操作栈顶时,volatile 关键字保证了 number 的值在此时是正确的,但是在执行 iconst_1、iadd 这些指令的时候,其他线程可能已经把 number 的值改变了,而操作栈顶的值就变成了过期的数据,所以 putstatic 指令执行后就可能把较小的 number 值同步回主内存之中。


总结如下:


在执行 number++这行代码时,即使使用 volatile 修饰 number 变量,在执行期间,还是有可能被其他线程修改,没有保证原子性。

发布于: 2 小时前阅读数: 6
用户头像

用故事、大白话讲解Java、分布式、架构设计 2018.05.06 加入

公众号:「悟空聊架构」 【个人博客】www.passjava.cn 【开源项目】基于 SpringCloud 的一套面试刷题系统 【Github】https://github.com/Jackson0714/PassJava-Platform

评论

发布
暂无评论
volatile 为什么不保证原子性