关键字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. git中 vi/vim的命令

    一.vi & vim 有两种工作模式: 1.命令模式:接受.执行 vi操作命令的模式,打开文件后的默认模式: 2.编辑模式:对打开的文件内容进行 增.删.改 操作的模式: 在编辑模式下按下ES ...

  2. 使用 Spring 2.5 注释驱动的 IoC 功能

    概述 注释配置相对于 XML 配置具有很多的优势: 它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作.如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO ...

  3. 调试程序时如何用syslog来打印信息

    转自:https://www.cnblogs.com/vigarbuaa/archive/2013/02/05/2892544.html Linux下C语言编程的-把程序输出信息加到系统日志里去关键词 ...

  4. BZOJ1899或洛谷2577 [ZJOI2005]午餐

    BZOJ原题链接 洛谷原题链接 解决这题得先想到一个贪心:吃饭慢的先排队. 并不会证明(感觉显然 设\(f[i][j][k]\)表示已经排好了前\(i\)人,第一个队伍需要花费的打饭时间为\(j\), ...

  5. UISwitch开关控件属性介绍以及获取开关状态并做出响应

    (1)UISwitch的大小也是固定的,不随我们frame设置的大小改变:也是裁剪成圆角的,设置背景就露马脚发现背景是矩形. (2)UISwitch的背景图片设置无效,即我们只能设置颜色,不能用图片当 ...

  6. 08. pt-find

    vim pt-find.cnf host=192.168.100.101port=3306user=adminpassword=admin pt-find --config pt-find.cnf d ...

  7. Lazarus下面的javascript绑定另外一个版本bug修正

    Lazarus下面的javascript绑定另外一个版本bug修正 从svn 检出的代码有几个问题 1.fpcjs.pas 单元开始有 {$IFDEF FPC} {$MODE delphi} {$EN ...

  8. Python3实战系列之三(获取印度售后数据项目)

    问题:续接上一篇.说干咱就干呀,勤勤恳恳写程序呀! 目标:实现第一个python程序的“Hello world!” 解决方案:新建一个项目Test,创建一个Test.py文件.在文件中实现打印出“He ...

  9. Mysql 常用增删改查命令集合教程

    创建:create      插入:insert 更新:update  查询:select 删除:delete 修改:alter     销毁:drop 创建一个数据库: create databas ...

  10. 【Apache】Apache服务的基本概念(二)

    Apache服务的基本概念 Apache安装请参照:[Apache]Apache服务的安装(一) 1.端口 apache默认监听TCP协议端口80端口 2.apache服务 apache服务默认会启动 ...