java中的volatile变量
同步与线程间通信:
通信
通信是指消息在两条线程之间传递。
既然要传递消息,那接收线程 和 发送线程之间必须要有个先后关系,此时就需要用到同步。通信和同步是相辅相成的。同步
同步是指,控制多条线程之间的执行次序。
线程间通信方式:
- 共享内存
共享内存指的是多条线程共享同一片内存,发送者将消息写入内存,接收者从内存中读取消息,从而实现了消息的传递。
但这种方式有个弊端,即需要程序员来控制线程的同步,即线程的执行次序。
这种方式并没有真正地实现消息传递,只是从结果上来看就像是将消息从一条线程传递到了另一条线程。
- 消息传递
顾名思义,消息传递指的是发送线程直接将消息传递给接收线程。
由于执行次序由并发机制完成,因此不需要程序员添加额外的同步机制,但需要声明消息发送和接收的代码。
java多线程内存模型:
所有线程都共享一片内存,用于存储共享变量;
此外,每条线程都有各自的存储空间,存储各自的局部变量、方法参数、异常对象。
volatile的使用:
public volatile boolean flag;
1)volatile在重排序(编译器、处理器在不改变程序执行结果的前提下,重新排列指令的执行顺序,以达到最佳的运行效率)中的使用:
在以下情况下,即使两行代码之间没有依赖关系,也不会发生重排序:
volatile读
- 若volatile读操作的前一行为volatile读/写,则这两行不会发生重排序
- volatile读操作和它后一行代码都不会发生重排序
volatile写
- volatile写操作和它前一行代码都不会发生重排序;
- 若volatile写操作的后一行代码为volatile读/写,则这两行不会发生重排序。
volatile保证共享变量的内存可见性:
volatile修饰了一个成员变量后,这个变量的读写就会比普通变量多一些步骤。
volatile变量写
当被volatile修饰的变量进行写操作时,这个变量将会被直接写入共享内存,而非线程的专属存储空间。volatile变量读
当读取一个被volatile修饰的变量时,会直接从共享内存中读,而非线程专属的存储空间中读。
通过对volatile变量读写的限制,就能保证线程每次读到的都是最新的值,从而确保了该变量的内存可见性。
volatile变量只能确保long、double读写的"原子性"(volatile在其他情况下是不能保证原子性的):
在Java中的所有类型中,有long、double类型比较特殊,他们占据8字节(64比特),其余类型都小于64比特。在32位操作系统中,CPU一次只能读取/写入32位的数据,因此对于64位的long、double变量的读写会进行两步。在多线程中,若一条线程只写入了long型变量的前32位,紧接着另一条线程读取了这个只有“一半”的变量,从而就读到了一个错误的数据。
为了避免这种情况,需要在用volatile修饰long、double型变量.
其实如果一个变量加了volatile关键字,就会告诉编译器和JVM的内存模型:这个变量是对所有线程共享的、可见的,每次jvm都会读取最新写入的值并使其最新值在所有CPU可见。所以说的是线程可见性,没有提原子性。
下面我们用一个例子说明volatile没有原子性,不要将volatile用在getAndOperate场合(这种场合不原子,需要再加锁,如i++),仅仅set或者get的场景是适合volatile的。
例如你让一个volatile的integer自增(i++),其实要分成3步:1)读取volatile变量值到local; 2)增加变量的值;3)把local的值写回,让其它的线程可见。这3步的jvm指令为:
mov 0xc(%r10),%r8d ; Load inc %r8d ; Increment mov %r8d,0xc(%r10) ; Store lock addl $0x0,(%rsp) ; StoreLoad Barrier
注意最后一步是内存屏障。
什么是内存屏障(Memory Barrier)?
内存屏障(memory barrier)是一个CPU指令。基本上,它是这样一条指令: a) 确保一些特定操作执行的顺序; b) 影响一些数据的可见性(可能是某些指令执行后的结果)。编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。
内存屏障(memory barrier)和volatile什么关系?上面的虚拟机指令里面有提到,如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。
为什么volatile变量用不同的线程访问修改后访问的结果会不一样(多数情况下不建议使用volatile变量提供可见性):
JVM在运行时内存分配汇总有一个内存区域称为虚拟机栈, 线程栈保存了线程运行时的信息,当线程访问某个对象的值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的值load到本地内存中(当前线程所分配内存区域),建立一个变量副本, 之后线程不再和对象在堆内存的变量有任何联系,而是直接修改副本的值,在修改完成之后自动把变量副本写回堆内存,这样堆内存的值就会改变:
read and load: 从主内存复制变量到当前工作内存
use and assign: 执行代码, 改变共享变量
store and write: 用工作内存数据刷新主内存相关内容
但在read and load 之后, 如果线程1对该volatile变量修改还未结束, 线程2也进行修改, 但修改的是最初的值, 将会导致并发的发生.
volatile变量使用的场景:
1).对变量的写入不依赖变量当前的值, 或者能确保只有单线程更新变量的值
2).该变量不会与其他状态变量一起纳入不变性条件中
3).在访问变量时不需要加锁
转载于:https://www.cnblogs.com/kexianting/p/8504236.html
java中的volatile变量相关推荐
- java中的Volatile 变量
Java 语言中的 volatile 变量可以被看作是一种 "程度较轻的 synchronized":与 synchronized 块相比,volatile 变量所需的编码较少,并 ...
- Java中的Volatile如何工作? Java中的volatile关键字示例
如何在Java中使用Volatile关键字 在Java采访中,什么是volatile变量以及何时在Java中使用volatile变量是Java 采访中一个著名的多线程采访问题 . 尽管许多程序员都知道 ...
- 面试:说说Java中的 volatile 关键词?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | Matrix海子 来源 | https://w ...
- 如何理解 JAVA 中的 volatile 关键字
如何理解 JAVA 中的 volatile 关键字 最近在重新梳理多线程,同步相关的知识点.关于 volatile 关键字阅读了好多博客文章,发现质量高适合小白的不多,最终找到一篇英文的非常通俗易懂. ...
- java中的Volatile关键字使用
文章目录 什么时候使用volatile Happens-Before java中的Volatile关键字使用 在本文中,我们会介绍java中的一个关键字volatile. volatile的中文意思是 ...
- java中二进制怎么说_面试:说说Java中的 volatile 关键词?
volatile 这个关键字可能很多朋友都听说过,或许也都用过.在 Java 5 之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 Java 5之后,volatile 关 ...
- 深入理解Java中的volatile关键字
在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...
- 【Groovy】Groovy 动态语言特性 ( Groovy 中的变量自动类型推断以及动态调用 | Java 中必须为变量指定其类型 )
文章目录 前言 一.Groovy 动态语言 二.Groovy 中的变量自动类型推断及动态调用 三.Java 中必须为变量指定其类型 前言 Groovy 是动态语言 , Java 是静态语言 ; 一.G ...
- Java中的宏变量,宏替换详解。
转载自 Java中的宏变量,宏替换详解. 群友在微信群讨论的一个话题,有点意思,特拿出来分享一下. 输出true false 来看下面这段程序,和群友分享的大致一样. public static vo ...
最新文章
- http://forensics.idealtest.org CASIA图像篡改数据库
- 从数据中心基础设施的视角来看 Facebook 机器学习的应用
- Delphi中的指针类型
- 人生苦短,Python值得!这些Pandas隐藏小技巧你知道吗?
- 海量网络存储系统原理与设计(三)
- python内置类属性_python常用内建属性大全
- IntelliJ IDEA中Spring Boot项目自定义Banner
- Hutool工具里,POST方法,body中传参的几种调用方法
- 如何删除计算机中的“天翼云盘”图标
- 特征的标准化和归一化
- python二进制处理详述
- 8个SVG动画JavaScript库
- 古体字与简体字对照表_简体字与繁体字对照表汇总
- 可用于龙芯2F的gmp
- PICK完成,但是状态依然为BACKORDER
- 5分钟摄像头抓拍一次,居家一天至少89次!尚德员工:连厕所都不敢上
- 解决在串口调试助手中每次复位后只能发送一次数据的问题
- mac下Xshell和Xftp | Royal TSX
- dns设置快速连接微软服务器,快速搭建Windows防污染DNS服务器——Pcap_DNSProxy(一)...
- 台式计算机有乱码如何解决,电脑出现乱码怎么修复 电脑字体乱码解决方法