标签:
关键字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并不是线程安全的。
标签:
原文地址:http://www.cnblogs.com/feijishuo/p/4546321.html