Java内存模型与volatile关键字
作者:网络转载 发布时间:[ 2015/6/12 15:38:30 ] 推荐标签:编程语言 内存
这便是因为race++操作不是一个原子操作,导致一些线程对变量race的修改丢失。若要使用volatale变量,一般要符合以下两种场景:
变量的运算结果并不依赖于变量的当前值,或能够保证只有单一的线程修改变量的值。
变量不需要与其他的状态变量共同参与不变约束。
使用volatile变量还可以禁止JIT编译器进行指令重排序优化,这里使用单例模式来举个例子:
/**
*单例模式例程一
*
*@author Colin Wang
*
*/
public class Singleton_1{
private static Singleton_1 instance=null;
private Singleton_1(){
}
public static Singleton_1 getInstacne(){
/*
*这种实现进行了两次instance==null的判断,这便是单例模式的双检锁。
*第一次检查是说如果对象实例已经被创建了,则直接返回,不需要再进入同步代码。
*否则开始同步线程,进入临界区后,进行的第二次检查是说:
*如果被同步的线程有一个创建了对象实例,其它的线程不必再创建实例了。
*/
if(instance==null){
synchronized(Singleton_1.class){
if(instance==null){
/*
*仍然存在的问题:下面这句代码并不是一个原子操作,JVM在执行这行代码时,会分解成如下的操作:
*1.给instance分配内存,在栈中分配并初始化为null
*2.调用Singleton_1的构造函数,生成对象实例,在堆中分配
*3.把instance指向在堆中分配的对象
*由于指令重排序优化,执行顺序可能会变成1,3,2,
*那么当一个线程执行完1,3之后,被另一个线程抢占,
*这时instance已经不是null了,会直接返回。
*然而2还没有执行过,也是说这个对象实例还没有初始化过。
*/
instance=new Singleton_1();
}
}
}
return instance;
}
}
/**
*单例模式例程二
*
*@author Colin Wang
*
*/
public class Singleton_2{
/*
*为了避免JIT编译器对代码的指令重排序优化,可以使用volatile关键字,
*通过这个关键字还可以使该变量不会在多个线程中存在副本,
*变量可以看作是直接从主内存中读取,相当于实现了一个轻量级的锁。
*/
private volatile static Singleton_2 instance=null;
private Singleton_2(){
}
public static Singleton_2 getInstacne(){
if(instance==null){
synchronized(Singleton_2.class){
if(instance==null){
instance=new Singleton_2();
}
}
}
return instance;
}
}
变量在有了volatile修饰之后,对变量的修改会有一个内存屏障的保护,使得后面的指令不能被重排序到内存屏障之前的位置。volalite变量的读性能与普通变量类似,但是写性能要低一些,因为它需要插入内存屏障指令来保证处理器不会发生乱序执行。即便如此,大多数场景下volatile的总开销仍然要比锁低,所以volatile的语义能满足需求时候,选择volatile要优于使用锁。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
Java性能测试有哪些不为众人所知的原则?Java设计模式??装饰者模式谈谈Java中遍历Map的几种方法Java Web入门必知你需要理解的Java反射机制知识总结编写更好的Java单元测试的7个技巧编程常用的几种时间戳转换(java .net 数据库)适合Java开发者学习的Python入门教程Java webdriver如何获取浏览器新窗口中的元素?Java重写与重载(区别与用途)Java变量的分类与初始化JavaScript有这几种测试分类Java有哪四个核心技术?给 Java开发者的10个大数据工具和框架Java中几个常用设计模式汇总java生态圈常用技术框架、开源中间件,系统架构及经典案例等

sales@spasvo.com