关键字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. mysql --secure-file-priv is set to NULL.Operations related to importing and exporting data are disabled

    --secure-file-priv is set to NULL. Operations related to importing and exporting data are disabledmy ...

  2. Linux locales

    一.简介   二.语法   三.实例 aptitude install locales dpkg-reconfigure locales ; vi /etc/default/locale more / ...

  3. 2.Mysql SQL基础

    2.Mysql SQL基础2.1 SQL简介 SQL(Structure Query Language)是结构化查询语言.2.2 SQL使用入门 2.2.1 SQL分类 SQL分为DDL.DML(DQ ...

  4. this指针 new 和delete

    指针类型的函数:函数的返回值是指针. 不要将非静态局部地址用作函数的返回值,离开函数后就失效了 在子函数中定义局部变量后将其地址返回给函数就是非法地址 在子函数中用new操作取得的内存地址返回给主函数 ...

  5. linux内核中hlist_head和hlist_node结构解析

    hlist_head和hlist_node用于散列表,分表表示列表头(数组中的一项)和列表头所在双向链表中的某项,两者结构如下: 1 2 3 struct hlist_head { struct hl ...

  6. Yii2 数据操作Query Builder查询数据

    Query Builder $rows = (new \yii\db\Query()) ->select(['dyn_id', 'dyn_name']) ->from('zs_dynast ...

  7. APM浅析

    APM(Application Performance Management & Monitoring)一种基于云的性能监控服务(SaaS),以非侵入式监听探针,收集应用关键指标,生成分析报表 ...

  8. Centos7 开启vsftpd

    Centos 1.开启DNS 可yum install (操作后要关闭) # vi /etc/resolv.conf# (INSERT)nameserver 8.8.8.8# (INSERT)表示按I ...

  9. Binary Prefix Divisible By 5 LT1018

    Given an array A of 0s and 1s, consider N_i: the i-th subarray from A[0] to A[i] interpreted as a bi ...

  10. 第一个spring boot 程序

    安装.运行.预览省略 错误1:8080端口被IIS占用,关闭它 Description: The Tomcat connector configured to listen on port 8080 ...