Java并发编程之验证volatile不能保证原子性

通过系列文章的学习,凯哥已经介绍了volatile的三大特性。1:保证可见性 2:不保证原子性 3:保证顺序。那么怎么来验证可见性呢?本文凯哥(凯哥Java:kaigejava)将通过代码演示来证明为什么说volatile不能够保证共享变量的原子性操作。

我们来举个现实生活中的例子:

中午去食堂打饭,假设你非常非常的饥饿,需要一荤两素再加一份米饭。如果食堂打饭的阿姨再给你打一个菜的时候,被其他人打断了,给其他人打饭,然后再回过头给你打饭。你选一荤两素再加一份米饭打完的过程被打断了四次耗时30分钟。你想想你自己的感受。是不是要疯了,要暴走了!其实,如果把从你点菜到阿姨给你打完饭这个过程,看着计算机的一个线程执行过程的话,那么在你点菜到你拿到饭菜这个过程是一个完整的,不能被打断的,这就是所谓的原子性。如果被多次打断的话想想你的心理,就知道程序如果在执行过程被打断后的结果了。

原子性操作的定义:

所谓的原子性操作就是线程对变量的操作一旦开始,就会一直运行直到结束。中介不会因为其他原因而切换到另一个线程。操作是不可分割的,在执行完毕之前是不会被其他任务或是事件中断的。一个操作或者是多个操作要么执行都成功要么执行都失败(可以结合数据库的原子性理解)。

怎么证明volatile修饰的共享变量就不能保证原子性呢?

模拟场景:

共享变量volatile int number=0;执行number++操作。使用多个线程多次调用。看看使用volatile修饰的number在执行结束后的结果是否是我们预期的结果。

我们分别用10个线程执行100次,50个线程执行1000次以及50个线程执行一百万次来看看结果。

先来看看变量是用volatil修饰的

再来看看主线程里面:

按照上面咱们规定的线程数量运行次数来看看咱们预期结果和实际运行结果:

我们分别用10个线程执行100次,50个线程执行1000次以及50个线程执行一百万次来

线程数量

执行次数

number预期结果

实际运行结果

10

100

10*100=1000

1000

50

1000

五万

49297

200

1000

二十万

194181

50

1000000

5千万

7246921

从上面表格中我们可以看到,即时共享变量用volatile修饰了。但是随着线程数量或者执行次数的增加,实际运行结果与预期结果相差越来越大。如果预期结果和运行结果一致则说明保证了原子性,但是从结果来看不是这样的。从而证明了volatile的第二个特性:不能保证原子性。

为什么从i++的运行结果上就能看出不保证原子性呢?

我们来分析:

正常来说200个线程,每个线程执行了1000次。最后应该输出的是:200*1000=20000.二十万。但是实际结果却不是二十万次。那说明了什么呢?请看下图:

说明:

主内存中有共享变量number的值是0,现在有4个CPU带着4个线程都从主内存中copy变量到自己的工作区。这个是CPU1先竞争到然后再线程1的工作区中执行了number++.执行后将number的值更新成了1,写回到主内存中了。这个时候正要或者正在通知其他CPU主内存中的number值变化了。CPU2和CPU3都收到通知了,将自己工作区的变量置为无效,重新从主内存获取到number=1的值。这个时候CPU4执行的也快,在还没有收到CPU1的通知的时候,就将自己运行后的number++的值也写回到了主内存中。其实这个时候,cpu1线程1的操作还在进行中,但是因为cpu4线程4的操作打断了线程1的操作。第一轮运行结果应该是4,但是因为线程4把线程1执行打断了,将线程1执行结果覆盖了。所以实际执行后的效果有可能是3或者2但是不可能是4.

从上分析结果,我们更能理解到volatile修饰的共享变量不能保证原子性了。因为有可能被其他线程打断执行。

怎么解决原子性问题呢?可以使用juc包下的atomic包下的对象就可以了。

Volatile的有序性证明,欢迎学习下一篇:《Java并发编程之验证volatile指令重排-理论篇》

欢迎关注凯哥公众号:凯哥Java(kaigejava)

Java并发编程之验证volatile不能保证原子性的更多相关文章

  1. Java并发编程之三:volatile关键字解析 转载

    目录: <Java并发编程之三:volatile关键字解析 转载> <Synchronized之一:基本使用>   volatile这个关键字可能很多朋友都听说过,或许也都用过 ...

  2. Java并发编程(二)如何保证线程同时/交替执行

    第一篇文章中,我用如何保证线程顺序执行的例子作为Java并发系列的开胃菜.本篇我们依然不会有源码分析,而是用另外两个多线程的例子来引出Java.util.concurrent中的几个并发工具的用法. ...

  3. Java并发编程基础之volatile

    首先简单介绍一下volatile的应用,volatile作为Java多线程中轻量级的同步措施,保证了多线程环境中“共享变量”的可见性.这里的可见性简单而言可以理解为当一个线程修改了一个共享变量的时候, ...

  4. Java并发编程学习:volatile关键字解析

    转载:https://www.cnblogs.com/dolphin0520/p/3920373.html 写的非常棒,好东西要分享一下 Java并发编程:volatile关键字解析 volatile ...

  5. 干货:Java并发编程系列之volatile(二)

    接上一篇<Java并发编程系列之synchronized(一)>,这是第二篇,说的是关于并发编程的volatile元素. Java语言规范第三版中对volatile的定义如下:Java编程 ...

  6. Java并发编程知识点总结Volatile、Synchronized、Lock实现原理

    Volatile关键字及其实现原理 在多线程并发编程中,Volatile可以理解为轻量级的Synchronized,用volatile关键字声明的变量,叫做共享变量,其保证了变量的“可见性”以及“有序 ...

  7. Java并发编程里的volatile。Java内存模型核CPU内存架构的对应关系

    CPU内存架构:https://www.jianshu.com/p/3d1eb589b48e Java内存模型:https://www.jianshu.com/p/27a9003c33f4 多线程下的 ...

  8. Java并发编程实战 03互斥锁 解决原子性问题

    文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 摘要 在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和 ...

  9. 【漫画】JAVA并发编程三大Bug源头(可见性、原子性、有序性)

    原创声明:本文转载自公众号[胖滚猪学编程]​ 某日,胖滚猪写的代码导致了一个生产bug,奋战到凌晨三点依旧没有解决问题.胖滚熊一看,只用了一个volatile就解决了.并告知胖滚猪,这是并发编程导致的 ...

随机推荐

  1. 喜欢B站的用户为何认同感超强?

    前几天视频弹幕网站哔哩哔哩(以下简称"B站"),正式登陆纳斯达克,股票代码"BILI".当日其CEOC兼董事长陈睿在B站的账号上传了上市视频. 打开视频,满眼& ...

  2. spring+mybatis+mysql5.7实现读写分离,主从复制

    申明:请尽量与我本博文所有的软件版本保持一致,避免不必要的错误. 所用软件版本列表:MySQL 5.7spring5mybaties3.4.6 首先搭建一个完整的spring5+springMVC5+ ...

  3. 前沿理论、反思创新、产学结合——你不能错过的WSDM 2016大会

    第九届ACM网络搜索与数据挖掘国际会议(ACM International Conference on Web Search and Data Mining,简称WSDM)已于上周(2月22日-25日 ...

  4. linux中nginx、mysql安装碰到的问题

    服务器到期新买了一台服务器,记录一下重新安装基本环境碰到了一些问题 安装nginx 1. 启动失败 403 forbidden nginx 解决方案:(个人使用直接用了root账号,修改对应nginx ...

  5. c++背包问题

    又鸽了好久…… 前言 博主刚刚学会背包问题不久,然后有一段时间没练习了 今天就来重新温习一下,顺手就写了这一篇博客. 好了,下面进入正题! 算法简介 背包问题是动态规划的一个分支 主要是分成了01背包 ...

  6. CSS——NO.3(CSS选择器)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  7. Pom.xml的依赖自动生成

    1.第一种用引入jar包的方法 网盘链接:https://pan.baidu.com/s/10HNjNeZc1d5QrFNtvLPWBA 提取码:oako 以上是整个文件直接用idea打开即可 imp ...

  8. startUML5.0中的tools下怎么没有java、c等选项

    这也是帮一个直系学妹弄得,哈哈~~~ 具体做法如下: 进入到StartUML\modules目录下,里面有很多文件夹,比如startuml-cpp.startuml-csharp等等, 进入到每个文件 ...

  9. ubuntu1804自带的vim和vi都是用什么版本?

    之前搜索vim一些命令时,经常看到有人说ubuntu自带的vim是是vim.tiny的,功能不全. 什么需要先卸载,再重装,真的是这样吗? 我查了一下,vim的版本号 vim --version vi ...

  10. openwrt sdk 编译工具 及 hello world

    需要先在 make menuconfig 中打开 sdk make -j4在 bin\targets\ramips\mt7620生成一个openwrt-sdk-ramips-mt7620_gcc-7. ...