public class Test {
boolean flag= false;
public void changeFlag(){
flag = true;
}
public void execute(){
if(flag){
System.out.println("execute....");
}
}
}
首先看上述代码:很简单,但是在这里如果有:线程A执行changeFlag方法之后,线程B再执行execute方法,试问,execute方法会不会打印出:execute....呢? (是的,这个是多线程的案例,由他来引入;)
答案是:不一定会;
原因:
一般情况下,会执行打印的方法,就说说不会打印的理由,
线程A在执行完flag = true;之后,还未完全退出线程A,这时线程B抢到CUP资源,开始执行execute方法,判断flag的值时,flag的值依然为false;
因为在java内存模型中,多线程之间的变量值是不可见的;每个线程都有自己独立的working memory(工作内存),里面保存该线程使用到的变量的副本;程序在执行之前,所有变量都存在主内存当中,线程内存会往主内存中拷贝一份变量的副本,线程执行结束后,会将副本变量值赋给主内存中对应的变量,然后主内存再将修改后的变量赋值到每个线程的副本中;所以线程之间变量值的传递需要通过主内存完成;而线程之间的变量(全局)是不能互相访问的;
回到当前实例,所以这就是线程A中的flag为false的原因;
在这里涉及到java内存模型知识;针对上述例子大致说说原因,推荐链接:https://www.cnblogs.com/rocomp/p/4780532.html
通过上述例子余留了一个问题:如何才能使得线程之间的变量(全局)可见?
java语言支持可见性的实现方法:
synchronize、volatile、final
这里主要说说volatile:
如果在上述例子中flag声明前加上volatile;那么答案就是肯定的了;
但是volatile就可以万能了?当然不是:如下例子:
public class Test{
public volatile int i=0;
public void test1(){
for(i;i<1000;i++){
system.out.println(i);
}
}
}
上述代码,在多线程运行该方法的情况下,是否会打印到1000呢,如果不会可以加到10000或更大,然后让线程睡三秒,执行结果却不是我们想要的(1.到1000依次打印);
打印结果坑定少于1000,那么为什么会出现此问题呢?
这里就是原子操作的原因:For Example 例如:
以下多线程对int型变量x的操作,哪几个需要进行同步:( )
A. x=y; B. x++; C. ++x; D. x=1;
博文转载:https://blog.csdn.net/encoder1234/article/details/52228224
看了上面的链接博文后,是否明白原因了呢? 是的,上述答案除了D外,全是;
volatile虽然在线程之间变量有可见性,但是却并没有保证原子操作;
假如x是一个long或者double类型,且当前系统是32位的,那么,D选项也需要同步;
java对long和double的赋值操作是非原子操作!!long和double占用的字节数都是8,也就是64bits。在32位操作系统上对64位的数据的读写要分两步完成,每一步取32位数据。这样对double和long的赋值操作就会有问题:如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。因此需要使用volatile关键字来防止此类现象。volatile本身不保证获取和设置操作的原子性,仅仅保持修改的可见性。但是java的内存模型保证声明为volatile的long和double变量的get和set操作是原子的。
那么怎样才能让全局变量既方便又安全呢?
请关注:CAS
推荐CAS博文:
https://www.jianshu.com/p/efb2024808a0
http://www.blogjava.net/xylz/archive/2010/07/04/325206.html
推荐volatile博文:
https://www.cnblogs.com/chengxiao/p/6528109.html
https://www.cnblogs.com/zhengbin/p/5654805.html