尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/52525724
 

在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型。

     (1)Java所有变量都存储在主内存中
     (2)每个线程都有自己独立的工作内存,里面保存该线程的使用到的变量副本(该副本就是主内存中该变量的一份拷贝)
   (1)线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接在主内存中读写
   (2)不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
线程1对共享变量的修改,要想被线程2及时看到,必须经过如下2个过程:
   (1)把工作内存1中更新过的共享变量刷新到主内存中
   (2)将主内存中最新的共享变量的值更新到工作内存2中
可见性与原子性
   可见性:一个线程对共享变量的修改,更够及时的被其他线程看到
   原子性:即不可再分了,不能分为多步操作。比如赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性。类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤:
① 取出a和b
② 计算a+b
③ 将计算结果写入内存
 
(1)Synchronized:保证可见性和原子性
    Synchronized能够实现原子性和可见性;在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。
(2)Volatile:保证可见性,但不保证操作的原子性
    Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。但volatile不保证volatile变量的原子性,例如:

  1. Private int Num=0;
  2. Num++;//Num不是原子操作

Num不是原子操作,因为其可以分为:读取Num的值,将Num的值+1,写入最新的Num的值。
    对于Num++;操作,线程1和线程2都执行一次,最后输出Num的值可能是:1或者2
   【解释】输出结果1的解释:当线程1执行Num++;语句时,先是读入Num的值为0,倘若此时让出CPU执行权,线程获得执行,线程2会重新从主内存中,读入Num的值还是0,然后线程2执行+1操作,最后把Num=1刷新到主内存中; 线程2执行完后,线程1由开始执行,但之前已经读取的Num的值0,所以它还是在0的基础上执行+1操作,也就是还是等于1,并刷新到主内存中。所以最终的结果是1

    一般在多线程中使用volatile变量,为了安全,对变量的写入操作不能依赖当前变量的值:如Num++或者Num=Num*5这些操作。
(3)Synchronized和Volatile的比较
    1)Synchronized保证内存可见性和操作的原子性
    2)Volatile只能保证内存可见性
    3)Volatile不需要加锁,比Synchronized更轻量级,并不会阻塞线程(volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。)
    4)volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化(如编译器重排序的优化).
    5)volatile是变量修饰符,仅能用于变量,而synchronized是一个方法或块的修饰符。
      volatile本质是在告诉JVM当前变量在寄存器中的值是不确定的,使用前,需要先从主存中读取,因此可以实现可见性。而对n=n+1,n++等操作时,volatile关键字将失效,不能起到像synchronized一样的线程同步(原子性)的效果。

Volatile 只保证可见性,并不保证原子性的更多相关文章

  1. JUC 并发编程--05, Volatile关键字特性: 可见性, 不保证原子性,禁止指令重排, 代码证明过程. CAS了解么 , ABA怎么解决, 手写自旋锁和死锁

    问: 了解volatile关键字么? 答: 他是java 的关键字, 保证可见性, 不保证原子性, 禁止指令重排 问: 你说的这三个特性, 能写代码证明么? 答: .... 问: 听说过 CAS么 他 ...

  2. 并发编程-CPU执行volatile原理探讨-可见性与原子性的深入理解

    volatile的定义 Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量.Jav ...

  3. 为什么volatile能保证有序性不能保证原子性

    对于内存模型的三大特性:有序性.原子性.可见性. 大家都知道volatile能保证可见性和有序性但是不能保证原子性,但是为什么呢? 一.原子性.有序性.可见性 1.原子性: (1)原子的意思代表着-- ...

  4. java——volatile的可见性不能保证线程安全

    volatile: 1.保证变量对所有线程的可见性(但是由于java里面的运算并非原子操作,导致volatile变量的运算在并发下一样是不安全的) 用代码试过,确实是这样的,原因:有可能同时多个thr ...

  5. 二、volatile关键字 - 内存可见性

    1.内存可见性 ​ (程序在运行时,jvm会为每一个执行任务的线程都分配一个独立的缓存,用于提高效率) ​ 我觉得可以这样来理解: ​ 内存:啥是内存?就是可以理解成电脑当中的内存条,程序创建个变量, ...

  6. Volatile 关键字 内存可见性

    1.问题引入 实现线程: public class ThreadDemo implements Runnable { private boolean flag = false; @Override p ...

  7. java多线程 -- volatile 关键字 内存 可见性

    内存可见性(Memory Visibility) 1 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其 ...

  8. 1.volatile关键字 内存可见性

    Java JUC 简介 在 Java 5.0 提供了 java.util.concurrent (简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括 ...

  9. threadlocal精髓是为每一个线程保证一个共享对象,保证一个,保证是同一个

    threadlocal精髓是为每一个线程保证一个共享对象,保证一个,保证同一个线程中是同一个共享对象. 如果是静态变量是共享的话,那必须同步,否则尽管有副本,还是会出错,故C错

随机推荐

  1. Spark链接hive时 “HikariCP” 问题

    IDE本地调试和spark-shell调试报错: Caused by: org.datanucleus.exceptions.NucleusUserException: The connection ...

  2. cocos2d-x 3.0正式版创建project笔记

    cocos2d-x 3.0正式版创建project笔记     不知道Beta版那个高大上的对话框哪里去鸟,正式版又回归到命令行,不知道触碰如此频繁的玩弄追随者的编程习惯是出于什么心理,假设不是为了这 ...

  3. NGUI的Tween动画的使用

    一,在创建Tween有,alpha,color,width,height,position,rotation,scale和transfrom这几种动画类型 1>alpha:颜色由浅变深(透明度) ...

  4. 分析abex'crackme#2

    文件地址:https://www.wocloud.com.cn/webclient/share/sindex.action?id=i9K_Br6TgE4gbyGIlYkffWKcRy5TUdZ8U6_ ...

  5. Linux知识-不断更新

    找到使用cpu最高的进程之使用cpu最高的线程的16进制号 shell命令行: ps -eo %cpu,pid | sort -n -k1 -r |head -n 1|awk '{print$2}'| ...

  6. Sass值列表

    所谓值列表 (lists) 是指 Sass 如何处理 CSS 中: margin: 10px 15px 0 0 或者: font-face: Helvetica, Arial, sans-serif ...

  7. List、Map、Set的存储特点?

    List.Map.Set的存储特点? List:以特定次序来持有元素,可有重复元素 Set:无法拥有重复元素,内部排序 Map:保存key-value值,value可多值

  8. spark的知识的链接

    IDEA 创建scala spark的Mvn项目:https://blog.csdn.net/u014646662/article/details/84618032 Spark详解03Job 物理执行 ...

  9. $emit 和 $on 进行平行组件之间的传值

    效果图: 注:$emit 和 $on 的事件必须在一个公共的实例上,才能够触发: $emit 触发 $on 接收 需求: 1.有A.B.C三个组件,同时挂载到入口组件中: 2.将A组件中的数据传递到C ...

  10. linux mail 发送邮件

    1.mail -s "Title“ 123456@163.com   只有主题,没有内容 2. 有邮件内容 echo "text" | mail -s "Tit ...