volatile关键字分析——可见性


声明:本文转载自https://my.oschina.net/u/1169079/blog/1542547,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

public class Thread02 implements Runnable { 	 /**volatile*/ boolean flg = true; 	@Override 	public  void run() { 		System.out.println(Thread.currentThread().getName()+":start"); 		while(flg){ 			//System.out.println("do something"); 		} 		System.out.println(Thread.currentThread().getName()+":end"); 	} 	 	public static void main(String[] args) { 		Thread02 test = new Thread02(); 		Thread t1 = new Thread(test); 		t1.start(); 		try { 			TimeUnit.SECONDS.sleep(1); 		} catch (InterruptedException e) { 			e.printStackTrace(); 		} 		test.flg=false; 	} }

上述代码中没有使用volatile关键字,但是在主线程中我们将flg至为false,但是结果如下:

 

public class Thread02 implements Runnable { 	volatile boolean flg = true; 	@Override 	public  void run() { 		System.out.println(Thread.currentThread().getName()+":start"); 		while(flg){ 			//System.out.println("do something"); 		} 		System.out.println(Thread.currentThread().getName()+":end"); 	} 	 	public static void main(String[] args) { 		Thread02 test = new Thread02(); 		Thread t1 = new Thread(test); 		t1.start(); 		try { 			TimeUnit.SECONDS.sleep(1); 		} catch (InterruptedException e) { 			e.printStackTrace(); 		} 		test.flg=false; 	} } 

我们将flg变量前加上volatile关键字,运行结果如下

我们将睡眠代码注释掉看结果,

public class Thread02 implements Runnable { 	volatile boolean flg = true; 	@Override 	public  void run() { 		System.out.println(Thread.currentThread().getName()+":start"); 		while(flg){ 			//System.out.println("do something"); 		} 		System.out.println(Thread.currentThread().getName()+":end"); 	} 	 	public static void main(String[] args) { 		Thread02 test = new Thread02(); 		Thread t1 = new Thread(test); 		t1.start(); //		try { //			TimeUnit.SECONDS.sleep(1); //		} catch (InterruptedException e) { //			e.printStackTrace(); //		} 		test.flg=false; 	} }

你会发现成功了,这是什么原因呢?

分析:

这与JVM实现有一些关系,线程读取CPU缓存有关系,在内存和CPU之间还有一层CPU缓存,CPU运行时会将内存中的变量读取并缓存到CPU缓存中,当主线程改变了内存中的变量时,CPU不在读取内存中的变量,而是直接读的自身缓存,产生了主线程与线程数据不一致的情况。加上volatile关键字是告诉CPU每次去内存中读一下,并缓存到本地(并不是不读缓存,是每次从内存中COPY),这样达到了一个变量可见的目的。将睡眠时间去掉会成功,说明主线程先于支线程修改了变量,并不是每一次都成功的,我的PC是4核的,所以基本每次都是成功的。

看下面一个问题,很诡异,将while循环中的注释掉的代码打开,去掉volatile关键字,开启睡眠

public class Thread02 implements Runnable { 	/*volatile*/ boolean flg = true; 	int count = 0; 	@Override 	public  void run() { 		System.out.println(Thread.currentThread().getName()+":start"); 		while(flg){ 			count++; 			System.out.println(count+":do something"); 		} 		System.out.println(Thread.currentThread().getName()+":end"); 	} 	 	public static void main(String[] args) { 		Thread02 test = new Thread02(); 		Thread t1 = new Thread(test); 		t1.start(); 		try { 			TimeUnit.SECONDS.sleep(1); 		} catch (InterruptedException e) { 			e.printStackTrace(); 		} 		test.flg=false; 	} }

看结果:

正常结束了,不是内存中变量不可见了么,怎么还正常结束呢?

分析:

肯定是CPU去读过内存了,具体什么时间不知道,这和CPU的工作机制有关,当CPU空闲的时候会读取内存数据缓存到本地,注意观察count值,我们再运行一遍

两次结果不一致,确切的说每次结果不一致,但是大概范围不会差距很大

本文发表于2017年09月24日 22:36
(c)注:本文转载自https://my.oschina.net/u/1169079/blog/1542547,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2554 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

万稳万当,不如一默。任何一句话,你不说出来便是那句话的主人,你说了出来,便是那句话的奴隶。

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1