关键字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. WEB框架Django之中间件/缓存/CBV/信号

    一Djano的中间件 1 中间件的概念 中间件顾名思义,是介于request与respose处理之间的一道处理过程,相对比较轻量级,并且全局上改变django的输入与输出.因为改变是全局, 所有需要谨 ...

  2. Query to find the eligible indexes for rebuilding

    Query to find the eligible indexes for rebuilding The following script can be used to determine whic ...

  3. tomcat/eclipse提速[z]

    在使用Eclipse开发项目过程中,一度使Eclipse陷入瘫痪状态,Tomcat启动项目时也异常缓慢,增加了超时限制并没有用,有时候项目根本运行不起来,简直让人崩溃,可能我电脑内存小(4G),配置低 ...

  4. c# dev treelist 总结

    1:去掉左侧顺序号列 2: EnableAppearanceFocusedCell 允许/否获得焦点的单格使用外观 设置TreeList的OptionsSelection属性: 3:设置TreeLis ...

  5. 17.Mysql分区

    17.Mysql分区分区是指根据一定的规则把一个表分解成多个部分,逻辑上仍是一张表,实际上由多个物理分区对象组成.分区对于应用是完全透明的,不影响业务逻辑和SQL编写.分区的优点: 可以存储更多的数据 ...

  6. js Map和Set

    Map Map是一组键值对的结构,具有极快的查找速度.JavaScript的对象有个小问题,就是键必须是字符串.但实际上Number或者其他数据类型作为键也是非常合理的.为了解决这个问题,最新的ES6 ...

  7. MacDev.GetArchOfLibrary

    1. static library How to check target architecture of a static library http://changhoward.blogspot.c ...

  8. LINUX查看网卡UUID

    有时我们不小心将/etc/sysconfig/network-scripts/ifcfg-eth0(可以通过此文件进行查看UUID)删除或者损坏,要重新编辑ifcfg-eth0文件时不知道网卡的UUI ...

  9. BZOJ1855或洛谷2569 [SCOI2010]股票交易

    一道单调队列优化\(DP\) BZOJ原题链接 洛谷原题链接 朴素的\(DP\)方程并不难想. 定义\(f[i][j]\)表示到第\(i\)天,手上持有\(j\)股时的最大收益. 转移方程可以分成四个 ...

  10. Python之路(第二十五篇) 面向对象初级:反射、内置方法

    [TOC] 一.反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它 ...