在解释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; }
}

最新文章

  1. ES6(四) --- 正则 Number Math
  2. WebStorm 有哪些过人之处?
  3. 头部固定下面滑动&amp;&amp;获取手机屏高
  4. sorl维护索引库sorl4j的使用
  5. Oracle11g空表导出方法
  6. 导入 from pdfminer.pdfinterp import process_pdf 错误
  7. 文字排版--删除线(text-decoration:line-through)
  8. android面试题之七
  9. Submission Details
  10. Chapter 13. Miscellaneous PerlTk Methods PerlTk 方法杂项:
  11. 沃森Mysql数据库修复工具
  12. Mac os下安装brew
  13. CSS速查列表-1-(background)背景
  14. Cinder组件
  15. day20 python常用模块
  16. zw·10倍速大数据与全内存计算
  17. System.out.println 报错: 只能运行在方法体内哦, 类里面只包含属性和方法哦,注意!
  18. Python新建/删除文件夹
  19. windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数
  20. Qt4_WebKit_例子

热门文章

  1. 读取yml 文件中的参数
  2. Equals和GetHashCode
  3. iOS面试-堆和栈的区别
  4. glide包管理工具
  5. 【AMAD】django-taggit -- 一个简单的,通用的django tagging模块
  6. mac go环境的安装和卸载
  7. elasticsearch数据库(ES)
  8. 《你必须知道的495个C语言问题》读书笔记之第4-7章:指针
  9. 在vue项目中,将juery设置为全局变量
  10. JS实现级联菜单