4、volatile关键字

关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制。当一个变量定义为volatile之后,它将具备两种特性,第一是保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。而普通变量不能做到这一点,普通变量的值在线程间传递需要通过主内存来完成,例如,线程A修改一个普通变量的值,然后向主内存进行回写,另外一条线程B在线程A回写完成之后再从主内存进行读取操作,新变量值才会对线程B可见。

 

然我们来看看一个有名的“双重检测锁单例模式”的实现:

 1 private static Singleton _instance;
 2 public static Singleton getInstance() {
 3    if(_instance==null) {                 //第一重检测
 4      synchronized(Singleton.class) {
 5         if(_instance==null) {            //第二重检测
 6            _instance=new Singleton();
 7         }
 8      }
 9    }
10    
11    return _instance;
12 }

两个问题:

一、为什么需要双重检测?

如果我们就是用一重检测加上同步代码块,当然能达到我们想要的效果,但是性能严重降低。因为我们仅仅只是需要在第一次请求才需要加锁,而后我们只需要将实例返回即可。

 

二、上面的程序是否存在问题?

当然存在问题。假设现在我们有4个线程(A、B、C、D)第一次都通过了第一层检测,当一个线程A进入到同步代码块后,对普通变量_instance进行写操作,但是这个写操作对于线程B、C、D有可能是不可见的。因为当B、C、D进行第二重检测时,也就是B、C、D要去主内存当中读取普通变量,但是这时候A线程在工作内存当中对_instance变量的修改,不一定就更新到了主内存当中。

 

为了使得线程A对_instance的写对B、C、D线程的读可见,那么变量_instance需要加上volatile关键字。因为JVM确保了volatile关键字修改的变量的写操作先行于(happen-before)后面的读操作。换一句话说,就是之前的写操作都能反应到读操作当中。但是如果在读了之后,再次被别的线程发生了写操作,然后该线程再将其写回主内存,就会导致数据不一致,因此volatile并不是线程安全的。

posted on 2015-06-02 14:07  飞机说之代码也疯狂  阅读(216)  评论(0编辑  收藏  举报