volatile关键字解析

volatile(Java提供的一种轻量级锁)保证了程序执行的有序性(禁止指令重排序),保证了操作变量的可见性,但是不能保证操作的原子性。

先看一段代码:

package demo_volatile;

public class VolatileTest {
	
	public boolean flag=false;
	
	//更改flag
	public void change(){
		this.flag=true;
	}
	
	//检查false,如果为true输出running……
	public void out(){
		if(flag){
			System.out.println("running……");
		}
	}

}

上面代码中在单线程环境下运行没有任何问题,程序顺序执行首先把flag初始化赋值为false,接着执行change方法重新赋值为true,最后执行out方法检查flag,发现flag为true执行输出语句。

但是如果在多线程环境下,就无法保证以上的运行结果了,可能在控制台上就会输出“running……”也有可能什么也不输出。为什么呢?在多线程条件下,一个线程执行的时候变量被修改了,但是其他的线程是不知道这个变量被修改的,依然会执行下去,这就是可见性。

 volatile保证可见性:                                                             

可见性:一个线程修改了共享变量的值,对于其他的线程这个变量是可见的。

在这里网上有好多文章说的很明白,参考了网上其他文章:对于java虚拟机,由于java的跨平台是因为每种平台对应的虚拟机不同,让java语言可以在不同平台上运行,而虚拟机为了保证处处运行维持了一个内存模型,这个内存模型屏蔽了各种平台和硬件的差异(一处编写处处运行),每个线程对应着一块本地内存,这个本地内存是线程的私有内存只对本线程开放,还有一块主内存,这个主内存保存着共享变量,对所有线程开放,所以线程都可以访问到里面的数据。所有的操作都是在线程的本地内存中(操作之前从主内存拿来数据之后写到主内存中),之后再同步到主内存中,不能直接对主内存中的数据进行操作

最好想的也是我们最常接触到的就是synchronized(同步),保证线程顺序执行,也就不会遇到上面的由于多线程条件下运行而导致的程序出现与我们理想状况下不同的输出。

用volatile关键字修饰变量,volatile关键字在前面提到了保证了程序运行中变量的可见性,看过上面的内存模型图,我们可以说volatile保证了线程之间的内存可见性,但是这个内存是私有的其他线程怎么能访问到呢?根本还是volatile关键字把变量等数据强制同步到了主内存中,同时屏蔽线程中本地内存的缓存,这样来保证程序的正确执行。

 volatile不能保证原子性:                                                      

volatile和synchronized都可以保证程序在多线程条件下的正确运行,但是synchronized非常影响效率,造成不必要的等待。synchronized类似于一种悲观锁,把东西一起都锁住,无论怎么都要同步执行,但是volatile关键字不能完全替代synchronized,volatile不能保证原子性,一般情况下,有涉及到原子操作的程序不能使用volatile,例如x++;这句程序不是一个原子操作,分为三个分别是先拿来x的值,再对x进行自增操作,最后赋值。在多线程运行的环境下用volatile依然不能保证得到正确结果。

例如:

package demo_volatile;

public class VolatileTest {
	
	public static volatile int num=0;
	
	public static void main(String[] args) {
		for(int i=0;i<20;i++){
			new Thread(){
				public void run(){
					for(int j=0;j<1000;j++){
						num++;
					}
				}
			}.start();
		}
		System.out.println(num);
	}
	

}

以上代码开启20个线程对num进行自增操作,利用num++语句,由于num++不是原子操作,所以最后得到的结果并不是我们想要的结果。

 volatile禁止指令重排序  :                                                    

volatile保证了程序指令操作的顺序,禁止指令的重排序

为什么要有指令的重排序?指令的重排序是为了优化性能而做的一种操作。但是这种优化可能对程序的执行造成不可遇见的结果

                int a=1;//1
		int b=2;//2
		int c=a+b;//3
类似于上面的程序,1和2的顺序可以颠倒因为他们没有依赖关系,但是3一定要是在1和2的后面执行的,3依赖于1和2的变量,在单线程下以上程序不会出现问题,但是在多线程环境下会出现问题,无法保证程序的顺序执行。

volatile关键字保证了程序中共享变量的线程可见性,禁止指令的重排序,但是没有保证原子性,对于复合语句(包含几个原子操作的程序)无法保证正确执行。

posted @ 2018-01-11 15:29  In_new  阅读(191)  评论(0编辑  收藏  举报