线程内存的可见性

版权声明:Please make the source marked https://blog.csdn.net/qq_31807385/article/details/83996378

先看下面的代码和运行的结果:

package hello_java;

public class MemoryVisiable {
    public static void main(String[] args) {
        ShareData02 shareData = new ShareData02();
        new  Thread(() ->{
            shareData.flag = true;
        }).start();

        while(true){
            if (shareData.flag){
                System.out.println("flag is true....");
                break;
            }
        }
        System.out.println("Main .... ");
    }
}

class ShareData02{
    public boolean flag ;
}

flag is true....
Main .... 

运行结果为如上。

对这个代码稍作修改:

package hello_java;

public class MemoryVisiable {
    public static void main(String[] args) {
        ShareData02 shareData = new ShareData02();
        new  Thread(() ->{
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            shareData.flag = true;
        }).start();

        while(true){
            if (shareData.flag){
                System.out.println("flag is true....");
                break;
            }
        }
        System.out.println("Main .... ");
    }
}

class ShareData02{
    public boolean flag ;
}

如此,如果运行当前的程序的话会发现,程序进入了死循环,为什么会发生死循环呢?下面就是解释。

内存可见性:

如上图中的内存模型,线程1会有一个与之对应的工作内存(这里的工作内存就是栈),线程1 和线程2 都想要操作主存中的某一个资源,但是不会直接操作主存中的资源而是将主存中的值复制到对应的工作内存中,在图的情景中,线程1对自己对应的工作内存中操作资源,将其修改为 a ,理论上讲,线程1 应该将值同步到主存中,但是线程1 可能还会操作该资源,这样频繁的交互,会影响效率,所以java暂时不会同步数据到主存中,所以,所以,此时线程2 ,无法获取到已经被修改的资源 也就是 a这个值。

内存的可见性:数据可能由于内存的可见性的原因在多个线程中看不见

我们上面的程序出现死循环的结果(两个线程,一个在读,一个在改)就是因为上述的原因,那么如何解决这个问题呢?先来看看效果:

package hello_java;

public class MemoryVisiable {
    public static void main(String[] args) {
        ShareData02 shareData = new ShareData02();
        new  Thread(() ->{
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            shareData.flag = true;
        }).start();

        while(true){
            if (shareData.flag){
                System.out.println("flag is true....");
                break;
            }
        }
        System.out.println("Main .... ");
    }
}

class ShareData02{
    public volatile boolean flag ;
}

flag is true....
Main .... 

由上面的内容可以看出,我们在共享数据的部分添加了 volatile 关键字,这个关键字可以做到,如果在工作内存中修改了共享数据,那么立即同步到内存中,那么其他的线程就能够获取(共享)到这个数据。接着我们来看看另外的一种解决方法:

package hello_java;

public class MemoryVisiable {
    public static void main(String[] args) {
        ShareData02 shareData = new ShareData02();
        new  Thread(() ->{
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            shareData.flag = true;
        }).start();

        while(true){
            if (shareData.flag){
                System.out.println("flag is true....");
                break;
            }
            System.out.println(shareData.flag);
        }
        System.out.println("Main .... ");
    }
}

class ShareData02{
    public  boolean flag ;
}


false
false
false
flag is true....
Main .... 

为什么会这样呢?原因在于println方法,我们查看该方法的源码:

    public void println(boolean x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

会发现该方法添加上了  synchronized 关键字,表明该方法加锁了,只要某线程加上了锁,他会将自己工作内存中的数据同步到主存中,然后将自己的工作内存中的数据清空,然后在从主存中读取数据到工作内存中。所以这里并不是println方法能够解决这个问题,而是加锁之后能解决这个问题。

猜你喜欢

转载自blog.csdn.net/qq_31807385/article/details/83996378
今日推荐