美文网首页
什么是“内存可见性”问题?

什么是“内存可见性”问题?

作者: Drew_MyINTYRE | 来源:发表于2022-06-15 09:18 被阅读0次
    /**
     * 演示可见性带来的问题
     */
    public class VisibilityProblem {
    
        int a = 10;
        int b = 20;
    
        private void change() {
            a = 30;
            b = a;
        }
    
    
        private void print() {
            System.out.println("b=" + b + ";a=" + a);
        }
    
        public static void main(String[] args) {
            while (true) {
                VisibilityProblem problem = new VisibilityProblem();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        problem.change();
                    }
                }).start();
    
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        problem.print();
                    }
                }).start();
            }
        }
    }
    

    下面我们运行这段代码并分析一下可能出现的情况。

    • 假设第 1 个线程,也就是执行 change 的线程先运行,并且运行完毕了,然后第 2 个线程开始运行,那么第 2 个线程自然会打印出 b = 30;a = 30 的结果。

    • 线程1先 start,并不代表它真的先执行,于是第 2 个线程先打印,然后第 1 个线程再去进行 change,那么此时打印出来的就是 a 和 b 的初始值,打印结果为 b = 20;a = 10。

    • 它们几乎同时运行,所以会出现交叉的情况。比如说当第 1 个线程的 change 执行到一半,已经把 a 的值改为 30 了,而 b 的值还未来得及修改,此时第 2 个线程就开始打印了,所以此时打印出来的 b 还是原始值 20,而 a 已经变为了 30, 即打印结果为 b = 20;a = 30。

    但是有一种情况不是特别容易理解,那就是打印结果为 b = 30;a = 10,我们来想一下,为什么会发生这种情况?

    如果出现了打印结果为 b = 30;a = 10 这种情况,就意味着发生了可见性问题:a 的值已经被第 1 个线程修改了,但是其他线程却看不到,由于 a 的最新值却没能及时同步过来,所以才会打印出 a 的旧值。发生上述情况的几率不高。

    那么我们应该如何避免可见性问题呢?

    我们如果给 a 和 b 加了 volatile 关键字后,无论运行多长时间,也不会出现 b = 30;a = 10 的情况,这是因为 volatile 保证了只要 a 和 b 的值发生了变化,那么读取的线程一定能感知到。

    除了 volatile 关键字可以让变量保证可见性外,synchronized、Lock、并发集合等一系列工具都可以在一定程度上保证可见性。

    synchronized 不仅保证了原子性,还保证了可见性。

    synchronized 不仅保证了临界区内最多同时只有一个线程执行操作,同时还保证了在前一个线程释放锁之后,之前所做的所有修改,都能被获得同一个锁的下一个线程所看到,也就是能读取到最新的值。因为如果其他线程看不到之前所做的修改,依然也会发生线程安全问题。

    相关文章

      网友评论

          本文标题:什么是“内存可见性”问题?

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