多线程--volatile
在解释volatile关键字之前,先说说java的指令重排以及代码的执行顺序。
指令重排:
public void sum(){
int x = 1;
int y = 2;
int x = x + 1;
int sum = x + y;
}
按代码的顺序,执行时先给x赋值再给y赋值,再执行x=x+1;最后求和。
由于x=x+1和sum=x+y是写操作不会进行指令重排,但是x=1与y=2是不互斥的;出于性能优化的考虑(对x赋值后直接对x进行操作可以节省重新获取x内存地址的时间),指令重排的最大可能是先对y赋值再对x赋值。
指令重排不会影响执行结果,是一种优化方式。
代码的执行顺序:单线程时volatile变量前后的代码不被编译器优化重排,代码顺序执行。多线程的并发情况下,对共享变量的操作顺序。由于多线程是竞争执行的,其执行顺序具有随机性,操作volatile变量时很有可能打破操作变量的原子性。
1.volatlie不保证对变量进行操作的原子性
对于基本数据类型的变量如int类型:多线程情况下的操作是从堆内存中读取变量值到线程栈,在线程栈中对变量值进行操作,再将变量值写回堆内存。打破原子性是指两个线程均从堆内存读数据再到写回堆内存的过程中间是可中断的,导致变量的值不可预测。例如:取出x=1;第一个线程进行x++操作,第二个线程进行x--操作,最终执行的结果有可能是0也有可能是2。
public class VolatileNotAtomic {
private static volatile long count = 0L;
private static final int NUMBER = 10000;
public static void main(String[] args) {
Thread substractThread = new SubstractThread();
substractThread.start();
for (int i = 0; i < NUMBER; i++) {
//synchronized(VolatileNotAtomic.class) {
count++;
//}
}
//等待线程结束
while (substractThread.isAlive()) {}
System.out.println("count : " + count);
}
private static class SubstractThread extends Thread {
@Override
public void run() {
for (int i = 0; i < NUMBER; i++) {
//synchronized (VolatileNotAtomic.class) {
count--;
//}
}
}
}
}
执行此段代码,运行结果不为0;说明volatile变量的操作不具有原子性。若要保证操作的原子性,打开注释的同步代码块即可。
volatile的语义仅保证指令不发生重排,保证变量的内存可见性。此处内存指主内存(堆内存)而不是工作内存(栈内存)。线程获取共享变量的值先从主内存空间读值加载到工作内存中,线程对变量值的读写操作后面都是发生工作内存中,并不保证与主内存值同步,此时在多线程并发时会导致变量值不一致。用volitale修饰变量后,强制从主内存读写变量值,保证主内存值的可见性。
2. volatile保证变量本身读写的原子性,读操作在写操作之后执行;典型应用场景:一写多读
下面以分析单例双检锁的实现过程来阐述volatile一写多读的应用。
对象的实例化顺序:
父类静态成员及静态代码块 ==> 父类静态方法 ==> 子类静态成员及静态代码块 ==>子类静态方法 ==> 父类普通成员及方法 ==> 父类构造方法 ==> 子类普通成员及方法 ==> 子类构造方法
对象在实例化时首先会向堆内存申请空间,然后执行上述实例化过程。
单例双检锁的实现如下:
public class Singleton {
private static Singleton singleton = null;
public static Singleton getInstance() {
if (null == singleton) {
synchronized(Singleton.class){
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
如上代码的问题在于:多线程执行时,如果线程1以执行到new Singleton(),但是对象实例化过程并未完成,此时线程2调用getInsatnce会获取到未完成实例化的对象(对象已分配内存可以被访问),导致结果错误。
解决上述问题只需在sinleton变量前加上volatile关键字即可。
public class Singleton {
private static volatile Singleton singleton = null;
public static Singleton getInstance() {
if (null == singleton) {
synchronized(Singleton.class){
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
多线程--volatile的更多相关文章
- C#多线程-volatile、lock关键字
volatile是C#中最简单的一种同步关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问时刻,最多有一个线程访问,以保证数据的完整性,虽与java中的synchroni ...
- Java多线程volatile和synchronized总结
volatile是轻量级的synchronized,在多处理器(多线程)开发中保证了共享变量的"可见性".可见性表示当一个线程修改了一个共享变量时,另外一个线程能读到这个修改的值. ...
- 多线程-volatile关键字和ThreadLocal
1.并发编程中的三个概念 原子性:一个或多个操作.要么全部执行完成并且执行过程不会被打断,要么不执行.最常见的例子:i++/i--操作.不是原子性操作,如果不做好同步性就容易造成线程安全问题. 可见性 ...
- java多线程-----volatile
谈谈Java中的volatile 内存可见性 留意复合类操作 解决num++操作的原子性问题 禁止指令重排序 总结 内存可见性 volatile是Java提供的一种轻量级的同步机制,在并发编程中, ...
- java多线程 -- volatile 关键字 内存 可见性
内存可见性(Memory Visibility) 1 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其 ...
- 看一遍就懂,详解java多线程——volatile
多线程一直以来都是面试必考点,而volatile.synchronized也是必问点,这里我试图用容易理解的方式来解释一下volatile. 来看一下它的最大特点和作用: 一 使变量在多个线程间可见 ...
- [.net 多线程]volatile 摘录
一.volatile 介绍 volatile 关键字指示一个字段可以由多个同时执行的线程修改. 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制. 这样可以确保该字段在任何时 ...
- [Java多线程] volatile 关键字正确使用方法
volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性,即多线程环境中,使用 volatile 关键字的变量仅可以保证不同线程读取变量时,可以读到最新修改的变量值,但是 ...
- Java 多线程 - Volatile关键字
作者: dreamcatcher-cx 出处: <http://www.cnblogs.com/chengxiao/> 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明 ...
随机推荐
- 【404】int main(int argc,char * argv[]) windows 下的使用
参考:int main(int argc,char * argv[]) windows 下的使用 参考:Theprogram can't start because libgcc_s_dw2-1.dl ...
- 面向对象ALV选择列
通过 gs_layout-box_fname = 'SEL'.设置选择行,不能取到 SEL列的值 找资料:作者:f122300349 来源:CSDN 原文:https://blog.csdn.ne ...
- HDU 1087 最大递增子序列
Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 ...
- Java中处理OPC寄存器数据类型
1. 在milo中,处理WORD等数据类型 例子如下: VariableNode node = client.getAddressSpace().createVariableNode( new Nod ...
- Unity学习笔记_控制人物移动+摄像机跟随
我想做的移动操作方式类似[流星蝴蝶剑].[龙之谷].[我的世界第三人称]的第三人称操作方式. 操作说明:W键会朝当前镜头方向前进,鼠标控制镜头旋转. 做前需知(先去稍微了解一下比较好): ①unity ...
- TMS320C6455小介绍
TMS320C6455是TI公司推出的的一款新型高性能单核定点DSP.它是TI公司基于第三代先进VeloviTI VLIW(超长指令字)结构开发出来的新产品,在通信,医疗图像,无线传输方面都可以大有作 ...
- WEB渗透技术之浅析路径遍历
1. 发送 http://www.nuanyue.com/getfile=image.jgp 当服务器处理传送过来的image.jpg文件名后,Web应用程序即会自动添加完整路径,形如“d://sit ...
- SQL Server 学习之环境搭建
SQL Server 环境搭建 说明:本文是sqlServer的安装和测试环境的搭建 版本是SQLServer 2005版,由于该版本只能在Windows7或者更低的系统上才能安装,更高的系统请安装S ...
- indows Eclipse Scala编写WordCount程序
Windows Eclipse Scala编写WordCount程序: 1)无需启动hadoop,因为我们用的是本地文件.先像原来一样,做一个普通的scala项目和Scala Object. 但这里一 ...
- 2019牛客暑期多校训练营(第三场)F Planting Trees 单调队列
F Planting Trees 题目链接 https://ac.nowcoder.com/acm/contest/883/F 题目描述 The semester is finally over an ...