在解释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的更多相关文章

  1. C#多线程-volatile、lock关键字

    volatile是C#中最简单的一种同步关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问时刻,最多有一个线程访问,以保证数据的完整性,虽与java中的synchroni ...

  2. Java多线程volatile和synchronized总结

    volatile是轻量级的synchronized,在多处理器(多线程)开发中保证了共享变量的"可见性".可见性表示当一个线程修改了一个共享变量时,另外一个线程能读到这个修改的值. ...

  3. 多线程-volatile关键字和ThreadLocal

    1.并发编程中的三个概念 原子性:一个或多个操作.要么全部执行完成并且执行过程不会被打断,要么不执行.最常见的例子:i++/i--操作.不是原子性操作,如果不做好同步性就容易造成线程安全问题. 可见性 ...

  4. java多线程-----volatile

    谈谈Java中的volatile   内存可见性 留意复合类操作 解决num++操作的原子性问题 禁止指令重排序 总结 内存可见性 volatile是Java提供的一种轻量级的同步机制,在并发编程中, ...

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

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

  6. 看一遍就懂,详解java多线程——volatile

    多线程一直以来都是面试必考点,而volatile.synchronized也是必问点,这里我试图用容易理解的方式来解释一下volatile. 来看一下它的最大特点和作用: 一 使变量在多个线程间可见 ...

  7. [.net 多线程]volatile 摘录

    一.volatile 介绍 volatile 关键字指示一个字段可以由多个同时执行的线程修改. 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制. 这样可以确保该字段在任何时 ...

  8. [Java多线程] volatile 关键字正确使用方法

    volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性,即多线程环境中,使用 volatile 关键字的变量仅可以保证不同线程读取变量时,可以读到最新修改的变量值,但是 ...

  9. Java 多线程 - Volatile关键字

    作者: dreamcatcher-cx 出处: <http://www.cnblogs.com/chengxiao/> 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明 ...

随机推荐

  1. 图解 HTTP 笔记(一)——了解 Web 及网络基础

    本章内容:Web 建立在何种技术之上,HTTP 协议如何诞生并发展? 一.Web 基于 HTTP 通信 Web 使用一种名为 HTTP (HyperText Transfer Protocol,超文本 ...

  2. kubernetes监控(12)

    一.Weave Scope 1. weave scope 容器地图 创建 Kubernetes 集群并部署容器化应用只是第一步.一旦集群运行起来,我们需要确保一起正常,所有必要组件就位并各司其职,有足 ...

  3. yum搭建LAMP环境

    LAMP=Linux+Apache(httpd)+Mysql(mariadb)+PHP Apache HTTP 服务器 2.4 文档:http://httpd.apache.org/docs/2.4/ ...

  4. .net断点续传

    IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下载请求时,服务 ...

  5. 阿里巴巴java规则p3c结合sonar使用

    sonar插件位置: jar包放至$SONAR_HOME/extensions/plugins下,重启sonar即可 如何在sonarqube的pmd插件中整合阿里开发规范 alibaba p3c s ...

  6. IIS中应用Application Request Route 配置负载均衡

    转自:https://blog.csdn.net/wucong60/article/details/84930234 简介ApplicationRequest Route(后面简称为ARR)是一个寄宿 ...

  7. Django:(01) 关于什么是Django?

    一.从 WSGI 开始了解 Django 在了解 Django 之前有必要先了解一下 Python 被用于 Web 开发的基础协议——WSGI. 在开发 web 应用时,我们不希望接触到TCP连接.H ...

  8. 【ARM-Linux开发】cmem模块/DVSDK2.0

    1. CMEM--高速缓存一致性问题的解决多核设计中,共享的二级高速缓存之间数据可能不一致,不同CPU内核的私有高速缓存也可能存在数据不一致,称为高速缓存的一致性问题.解决一致性问题的方法从整体上分可 ...

  9. tushare获取股票每日重要的基本面指标数据,并存入Elasticsearch

    tushare是一个开放的,免费的金融数据平台,包含沪深股票数据,指数数据,基金数据,期货数据,期权数据,债券数据,外汇数据,港股数据,行业经济数据,宏观经济数据以及新闻快讯等特色数据.其中以沪深股票 ...

  10. OpenCV.20190628

    1.OpenCV提取ORB特征并匹配 - 简书.html(https://www.jianshu.com/p/420f8211d1cb) OpenCV提取ORB特征并匹配 - 简书.html(http ...