JAVA线程之内存可见性

来源:互联网 发布:tensorflow gpu whl 编辑:程序博客网 时间:2024/06/01 17:42

今天学到了java线程之内存可见这一章。现在来进行总结,理一下思维;

首先什么叫做内存可见性,内存可见性就是指我们在多线程的程序中,一个共享变量的修改能够被另一个线程所能及时看到,并能及时引用,我觉得这就叫做内存可见性;

我们都知道在一个线程中共享变量的值都存储在主内存中,而当我们启动线程的时候,jvm会为我们再线程中创建一个工作内存,这个工作内存中,存储的相当于是主内存中的副本。当线程1的值要发生修改时,它会先去修改工作内存1中的值,然后再去刷新主内存中的值。但是这样做就会存在一些问题,比如线程1工作内存的值,刚好发生了修改。而线程2工作内存的值也发生了修改,他们都会去刷新主内存的值,假设两个工作同时执行,主内存的值有可能发生一次,这就是我们所说的并发特性里面的内存不可见性。下面我们写一个程序来分析一下

private int sum; //全局变量
private class NewThread extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
sum++;
}
System.out.println(sum);
super.run();
}
}
public static void main(String[] args) {
MyThread mt=new MyThread();
for(int i=0;i<10;i++){
mt.new NewThread().start();
}
}

最后打印一下结果,大部分都是10的倍数。

10
30
20
40
50
70
60
90
100
80

但是我们多运行几遍就会有其他的不同结果,这就是由于并发所导致的。

java的不可见性主要是有两种原因导致的,一个是并发,还有一个就是重排序,上面的一个例子就是并发,下面我们来看一个重排序的案例

public class SynThread {
public boolean flag;
public int number;
public int sum;

public void read(){
flag=true; //1.1最开始执行的把flag的值改变为true;
number=3; //1.2把number的值变为3
}
public void write(){
if(flag){ //2.1验证flag是否为true
sum=number*2; //2.3为true则执行下一步把number的值乘以2得到sum
}
System.out.println(sum); //2.3打印结果应该为6
}
private class myThread extends Thread{
private boolean flag;


public myThread(boolean flag) {
super();
this.flag = flag;
}
@Override
public void run() {
if(flag){
write();
}else{
read();
}

}
}
public static void main(String[] args) {
SynThread st=new SynThread();
st.new myThread(true).start();
st.new myThread(false).start();
}

按照正常的逻辑来说打印的sum值应该为6,但是也有其例外的地方,那就是java的重排序,只要执行

结果符合as-if-serial语义

as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。比如说语句没有算法关系的,没有依赖关系的就有可能会执行重排序;上面的程序有可能按照顺序执行,也有可能先执行1.2,1.1,2.2,2.1,2.3,也有可能先执行2再执行1,这样结果就会发生改变其值就会变成0;

我们可以通过两个简单的方法来实现java内存的可见性,一个是同步关键字.synchronized。

加上这个关键字之后,不管程序怎么执行,他都会保证一个线程执行完后,才会执行下一个程序,而且他具有原子性,所以能够实现能存可见性,还有一个就是通过volitile关键字。这个关键字不能保证同步但能实现重排序,其原理就是用它修饰的变量,在线程内存修改的时候,必须马上去刷新主内存中的值,而当其它线程要取得主内存中的值时,JVM必须保证其内存中的值必须是最新值.所以可以保证其内存可见性.

0 0
原创粉丝点击