美文网首页
volatile详解(一)(可见性)

volatile详解(一)(可见性)

作者: iMikasa_ | 来源:发表于2021-08-29 12:36 被阅读0次

    再介绍volatile之前,我们了解一下有什么状况,让我们需要用到volatile。

    这就要说起导致线程安全的——可见性了。
    举个例子

    //线程1执行的代码
    int i = 0;
    i = 10;
    //线程2执行的代码
    j = i;
    

    假若执行线程1的是CPU1,执行线程2的是CPU2。由上面的分析可知,当线程1执行 i =10这句时,会先把i的初始值加载到CPU1的高速缓存中,然后赋值为10,那么在CPU1的高速缓存当中i的值变为10了,却没有立即写入到主存当中。

    此时线程2执行 j = i,它会先去主存读取i的值并加载到CPU2的缓存当中,注意此时内存当中i的值还是0,那么就会使得j的值为0,而不是10.

    这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。

    下面是展现问题的具体代码

    package com.imikasa.test1;
    
    public class TestThread2 {
        static boolean flag = true;
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(()->{
                while(flag){
    //                System.out.println("hello");
                }
            }).start();
            System.out.println("1s后停止线程");
            Thread.sleep(1000);
            flag = false;
        }
    }
    

    程序运行之后会一直运行是循环,主线程修改的flag的值不会被其他线程知道(cpu缓存引起),可见性问题通常都是由一个线程修改共享变量,其他线程读取那个共享变量引起的...

    如何保证可见性

    Java提供了volatile关键字来保证可见性。

    当一个共享变量被volatile修饰时static volatile boolean flag = true;,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

    而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

    值得注意的是,在上面我们测试展示可见性问题的代码中,我们把死循环中的打印语句注释去掉,在运行,发现一秒后,死循环结束,保证了可见性,这是为什么呢?
    原因很简单,我们看println()的源码就知道了


    println()

    因为通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。但是缺点是synchronized属于重量级操作,性能相对更低

    相关文章

      网友评论

          本文标题:volatile详解(一)(可见性)

          本文链接:https://www.haomeiwen.com/subject/qgmwiltx.html