关键字volatile虽然增加了实例变量在多个线程之间的可见性,但它却不具备同步性,那么也不具备原子性。

测试

package com.cky.thread;

/**
* Created by edison on 2017/12/9.
*/
public class MyThread extends Thread{
volatile public static int count;
private static void addCount() {
for(int i=0;i<100;i++) {
count++;
}
System.out.println("count="+count);
} @Override
public void run() {
super.run();
addCount();
}
}
package com.cky.test;

import com.cky.thread.MyThread;

/**
* Created by edison on 2017/12/9.
*/
public class Run {
public static void main(String[] args) { MyThread[] myThreads = new MyThread[100];
for (int i = 0; i < 100; i++) {
myThreads[i] = new MyThread(); }
for (int i = 0; i < 100; i++) {
myThreads[i].start();
}
}
}
count=8600
count=8500
count=8400
count=8200
count=8300
count=8000
count=7800
count=7700
count=7500

结果:线程异步

更改加上关键字synchronized

package com.cky.thread;

/**
* Created by edison on 2017/12/9.
*/
public class MyThread extends Thread{
volatile public static int count;
synchronized private static void addCount() {
for(int i=0;i<100;i++) {
count++;
}
System.out.println("count="+count);
} @Override
public void run() {
super.run();
addCount();
}
}
count=9400
count=9500
count=9600
count=9700
count=9800
count=9900
count=10000

在本例中,如果在方法private static void addCount()前加入synchronized同步关键字,也就没有必要再使用volatile关键字来声明count变量了。

关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值的使用,也就是多线程读取共享变量时可以获得最新值的使用。

关键字volatile提示线程每次从共享变量中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性,但在这里需要注意的是

如果修改实例变量中的数据,例如i++,也就是i=i+1,则这样的操作其实不是一个原子操作,也就是非线程安全,表达式i++操作步骤

1)从内存中取出i值

2)计算i值

3)将i值写到内存中

假设在第2步计算值的时候,另外一个线程也修改i值,那么这个时候会出现脏读,解决方法就是使用synchronized关键字,所以volatile本身并不处理数据的

原子性,而是强制对数据的读写及时影响到主内存。

如下图演示的是volatile出现非线程安全的原因

结论:

1)read 和load阶段:从主内存复制变量到当前线程工作内存

2)use和assign阶段:执行代码,改变共享变量值

3)store和write阶段:用工作内存数据刷新主内存对应的变量的值

在多线程环境中。use和assign是多次出现的,但这个操作不是原子性,也就是在read和load之后,如果主内存中的count变量发生修饰之后,线程

工作内存中的值已经加载,不会产生对应的变化,也就是私有内存和公共内存中的值不同步,所以计算出来的结果和预期不一样,也就出现了线程不安全

对于volatitle修饰的变量,JVM只是保证从主内存加到线程工作内存中的值是最新的,例如线程1和线程2在进行read和load操作时,发现主内存中的

count值都是5,那么都会加载这个最新值,也就是说,volatile关键字解决的是变量读时的可见性问题,但无法保证原子性,对于多线程访问同一个实例变量

还是需要加锁同步。

2.3.4volatile的原子性的更多相关文章

  1. Java并发_volatile实现可见性但不保证原子性

    读后感 介绍了volatile实现可见性的基本原理 介绍了volatile不能实现原子性的示例,volatile复合操作不能实现原子性,读取值后在自增前改值可能被其它线程读取并修改,自增后刷新值可能会 ...

  2. volatile不能保证原子性

    1.看图自己体会 2.体会不了就给你个小程序 package cs.util; public class VolatileDemo { private volatile int count =0; p ...

  3. 为什么volatile不能保证原子性而Atomic可以?

    在上篇<非阻塞同步算法与CAS(Compare and Swap)无锁算法>中讲到在Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值 ...

  4. 【系统架构】缓存Memcache 使用原子性操作add,实现并发锁

    原文地址 memcache中Memcache::add()方法在缓存服务器之前不存在key时, 以key作为key存储一个变量var到缓存服务器.我们使用add来向服务器添加一个键值对应,如果成功则添 ...

  5. redis原子性读写操作之LUA脚本和watch机制

    最近在开发电商平台的子系统--储值卡系统,系统核心业务涉及到金额消费以及库存控制,因此为了解决建立在内存上高并发情况下的事务控制,使用了spring封装的RedisTemplate执行lua脚本进行原 ...

  6. java并发编程学习笔记(一)初识并发原子性

    1.并发的意义 现在是一个多核的时代,并发的存在意义就是为了能够充分利用多核计算机的优势,提高程序的运行效率: 2.并发的风险 竞争-----多个线程对内存数据数据进行读写操作时,对数据处理结果的一个 ...

  7. Objective-C 中,atomic原子性一定是安全的吗?

    我们在学习OC的时候认为,atomic使用了原子性,保证了线程安全,事实真的是这样吗? nonatomic的内存管理语义是非原子性的,非原子性的操作本来就是线程不安全的,而atomic的操作是原子性的 ...

  8. 将对象的所有属性名放到一个数组中 || 获得对象的所有属性名 || return;不具有原子性 || 怎样自己制作异常|| 判断对象有没有某个属性 || 当传递的参数比需要的参数少的时候,没有的值会被赋予undefined || 获得函数实际传递的参数 || 怎么用函数处理一个对象 || 用一个名字空间定义一个模块所有的函数 || 给一个对象添加方法

    获得对象的所有属性名 || 将对象o的所有属性名放到数组中 var o = {x:1,y:2,z:3}; var arr = []; var i = 0; for(arr[i++] in o){};/ ...

  9. 缓存MEMCACHE 使用原子性操作add,实现并发锁

    memcache中Memcache::add()方法在缓存服务器之前不存在key时, 以key作为key存储一个变量var到缓存服务器.我们使用add来向服务器添加一个键值对应,如果成功则添加,否则说 ...

随机推荐

  1. 移动端IOS和androi及浏览器js判断[转载]

    转载自:http://www.niutifa.com/?p=561 移动端IOS和androi及浏览器js判断: <script type="text/javascript" ...

  2. Error starting daemon: error initializing graphdriver: driver not supported

    Error starting daemon: error initializing graphdriver: driver not supported systemctl stop docker rm ...

  3. ListView的自定义适配器及其优化(listView序号混乱问题的处理)

    ListView是最常使用的android组件之一,关于listView的优化问题刚刚了解了一些,在这里做出总结. PS:如果想让ListView中的item根据数据内容显示item的大小,需要在it ...

  4. Find Amir CodeForces 805C

    http://codeforces.com/contest/805/problem/C 题意:有n个学校,学校的编号是从1到n,从学校i到学校j的花费是(i+j)%(n+1),让你求遍历完所有学校的最 ...

  5. 深浅copy和字符串细节方法

    copy a=[1,2,3]b=aid(a)55499272id(b)55499272 id()就是查看内存地址,是不是同一个对象. c=a.copy()id(c)57940040 可见copy()出 ...

  6. P1083龙舟比赛

    题目如下: 现在正在举行龙舟比赛,我们现在获得了最后冲刺时的俯视图像,现在你要输出各条龙舟的名次. 这张图像由r行c列的字符组成,每行的最左边的字符表示起点,所以字符为'S',最右边的字符为'F'.并 ...

  7. c# 反编译后窗口控件在vs中打不开的修改方法

    DialogResult.Cancel System.Windows.Forms.DialogResult.Cancel DialogResult.OK System.Windows.Forms.Di ...

  8. Getting svn to ignore files and directories

    August 27, 2013Software Developmentresources, subversion, svn, tutorial, version control Who knew it ...

  9. Java 算法(一)贪心算法

    Java 算法(一)贪心算法 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 一.贪心算法 什么是贪心算法?是指在对问题进行求 ...

  10. 如何从jks文件中导出公私钥

    1.从JKS转换到PKCS12 #keytool -importkeystore -srckeystore <MY_KEYSTORE.jks> -destkeystore <MY_F ...