多线程--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/> 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明 ...
随机推荐
- Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.5:test
解决方法: 打包跳过测试有两种方法 一是命令行 mvn clean package -Dmaven.test.skip=true 二是写入pom文件 <plugin> <groupI ...
- Hystrix-基本概念(设计原则和两种隔离技术)
一.Hystrix是什么在微服务的架构系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务.有的时候某些依赖服务出现故障也是很正常的.Hystrix可以让我们在对服务间的调用进行控制 ...
- Python3 Selenium自动化web测试 ==>FAQ:日期格式和日期字符串格式相互转换
学习目的: 掌握python的基础应用 场景: 生成的测试日报需要加上时间戳作为唯一标志,免得文件覆盖,过往的文件丢失 因为os.rename方法要求文件名必须拼接的都是字符串 代码释义: # 日期转 ...
- Mac下Unity使用Jenkins自动化打包
重要的事情说三遍:不要使用jenkins dmg安装包直接安装,用brew安装 PS:会有权限问题 重要的事情说三遍:不要使用jenkins dmg安装包直接安装,用brew安装 重要的事情说三遍: ...
- MyBatis接口式编程
MyBatis使用接口连接数据库 之前学习了如何传统的使用MyBatis连接数据库,今天学习如何使用更方便快捷的方式练级数据库.使用接口连接数据库 https://www.cnblogs.com/li ...
- window环境下zookeeper的安装(自用---仅供参考)
转自: https://www.cnblogs.com/ysw-go/p/11396343.html 第一部分:单机模式 1)下载地址:http://www.pirbot.com/mirrors/ap ...
- 最新 米哈游java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.米哈游等10家互联网公司的校招Offer,因为某些自身原因最终选择了米哈游.6.7月主要是做系统复习.项目复盘.LeetCo ...
- JavaScript里的类和继承(转)
转自: http://www.h5cn.com/js/jishu/2016/0121/17634.html js与大部分客户端语言有几点明显的不同: JS是 动态解释性语言,没有编译过程,它在程序运行 ...
- idea设置方法注释
1. File-->Settings-->Editor-->Live Templates 1.1 新建Template Group: 命名为 methodNote 1.2 在meth ...
- 剑指offer24:二叉树中和为输入整数值的所有路径。(注意: 在返回值的list中,数组长度大的数组靠前)
1 题目描述 输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.(注意: 在返回值的list中,数组长 ...