关于volatile关键字与内存可见性的一点理解
在日常写java代码的环境下(不是研究计算机理论的情况)是会观测到内存可见性所带来的一些问题,比如下面这段代码:
public class ReorderingDemo { static boolean flag = false; public static void main(String[] args) throws Exception { new Thread(new Runnable() { @Override public void run() { while(!flag){}; } }).start(); Thread.sleep(100); flag=true; System.out.println(100); } }
输出结果:
观测到打印出了100,证明flag=true已被执行过,而程序并没有被终止,说明子线程并没有观测到flag被修改后的值,出现了可见性的问题。为了避免可见性,在相应变量上加上volatile关键字,
public class ReorderingDemo { static volatile boolean flag = false; public static void main(String[] args) throws Exception { new Thread(new Runnable() { @Override public void run() { while(!flag){}; } }).start(); Thread.sleep(100); flag=true; System.out.println(100); } }
运行结果:
对boolean的修改会理解被子线程观察到;
关于其他一些情况,建议移步
http://www.importnew.com/24082.html
讲的马马虎虎,现在谈谈自己感悟,如有问题,请及时指出,我会马上改正:
对于一条java指令
像这样的:
(1) flag=true;
十分简单,稍微复杂一点:
new Thread(new Runnable() { @Override public void run() { while(!flag){}; } }).start();
对于一款java虚拟机来说,他能做的优化十分有限,我jvm优化了解不是很多,但我意识到什么能成,什么不能成;
对flag=true;这种最简单的赋值操作,类型boolean是基本类型,大小固定,类型固定,赋值操作的值true是一个静态值,对于像这样的操作,jvm在保证结果的前提下,很有可能做出某种优化;但是对于第二种方式十分复杂的调用,jvm做的就很有限了;
像上面那种因为可见性(或称内存栅栏)所带来的失败这种操作,无论在什么时候我们都是不期望,既然如此,为什么java官方不取缔呢,废除volatile关键字,无论什么时候线程A都能立即看到线程B修改后的值,这不很好吗?而且volatile所带来的性能损失挺低的(该观点出自java并发编程实战);或许这只是我们的一厢情愿,或许在不同jvm上还会有各种各样优化(server模式与client模式就相差很大),以及在我们看来损耗的性能很小但在jvm角度上损害性能很大等等一系列原因以至于想完全废除volatile关键字是不可能的;或许随着java发展会出现别的措施;
至于sync关键字,能解决同步性与可见性,除了性能损耗上去了,但用起来还是十分好用的,而且现在java官方出的Executor框架以及其他一些并发框架,使得开发效率越来越高,因此上设计越来越好的代码架构变得更加重要,毕竟优秀的代码结构能带来的不仅是速度上的提升,更是开发效率上的提升,对那些经常关注各种并发不放,并且经常提问一些本就存疑自己也没弄明白的知识进行提问的面试者及一些初学者,得反思下了;
还有关于指令重排序的问题,两个关键词“指令” 与 “重排序”,到底这个指令是怎么规定的,是一条java代码(有简单,有复杂的),一条jvm指令(jvm也是具有指令的,对应编译期指令重排序),一条机器指令(对应运行期指令重排序)?至于jvm指令转化成机器指令是否还存在其他一些中间环节可能造成重排序,我不知道。但是直觉告诉我该关键字 “指令” 应该指的不是java代码,但对于一些低级java代码像flag=true; i++; i=1+3;等简单java代码,在编译期会一条java代码转化为固定且能被jvm判断出来的的那么一条至少数几条jvm指令的java代码能进行在java代码级别进行重排序也说不定,对于观测这种指令重排序的行为,像我这样的也只能在java代码级别去观察,然而不幸的是,我至今还没找出能正确表示指令重排序的代码写法,网上的代码其实很多代码并不会观测到,以及潜在的jvm优化(server模式)或者某些行为可以用其他观点进行辨别,比方说java并发编程实战给出的经典例子中也可以用可见性去解释等等,当然它给的例子我也没观测到。如果有哪位老哥有确切的代码观测方法或者实例或者相关博客,请联系下我,十分感谢!!!!目前我将指令重排序理解为jvm指令级别并且java代码不会观测到指令重排序的效果