请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

关于双重检测(工作内存、主内存)

老师,您好,我现在有一点儿懵逼,工作内存的对变量副本的修改什么时候会同步会主内存呢?
比如双重检测实现的单例模式:

public class Util {
    private static Util instance;

    public static Util getInstance() {
        if (instance == null) {
            synchronized (Util.class) {
                if (instance == null) {
                    instance = new Util();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        Util.getInstance();
    }
}

这里instance变量需要volatile修饰是因为instance = new Util();可能发生指令重排。但是我现在一想,即使A线程在执行instance = new Util();操作的时候,发生了重排,先把内存空间地址设置到了instance变量,然后再初始化内存空间。但是因为给instance变量赋值的操作是在A线程内发生的,这时赋值的目标应该是instance变量在A线程工作内存的副本才对啊,当B线程来获取instance的时候,如果A线程没有把最新值(不论是有没有被初始化完成的对象)同步到主内存,那么B线程从主内存获取instance的结果应该也是空才对啊,就不会出现获取到一个没有初始化好的对象的情况啊。
谢谢老师解答!

正在回答

1回答

Jimin 2019-05-07 21:53:59

你好,你的假设是有问题的。
这段代码可能执行出错的场景在于:首先是a线程设置了instance在内存里的空间地址,然后分配相关内存空间。在设置完instance空间地址时,其他线程就已经可以看到了,而且这时instance已经不为空了。b线程如果刚好在这个节点调用这个方法时,判断instance不为空,就会直接返回这个实例,b线程不会继续做初始化,这时instance就会被其他方法拿到并使用,而由于instance并没有分配实际的内存工具(本身分配可能很慢,即a线程第二步一直没做完),这时实际使用instance就会出错了,因为没有实际分配内存。
这段代码不会肯定出问题,而是只有在特殊的场景下才会触发一些异常的可能。你的例子如果没对上这种特殊的场景,就看不出问题所在了

0 回复 有任何疑惑可以回复我~
  • 提问者 慕神2874530 #1
    谢谢老师解答,我就是在想,现在就是出现了这个场景:先分配地址,再初始化内存。但是a线程先设置了instance在内存里的空间地址,为什么其它线程可以立即看到呢?这个设置操作是在a线程的工作内存执行的,isntance只是在a线程的工作内存才不为空吧,不是要等a线程将工作内存的值同步到主内存,其它线程再从主内存去获取才行吗?如果a线程没有将instance的值同步回主内存,其它线程如何能看到instance不为空呢?谢谢!
    回复 有任何疑惑可以回复我~ 2019-05-08 09:52:02
  • Jimin 回复 提问者 慕神2874530 #2
    懂了就好。高版本的jdk里貌似对这种提前可见做了修正,这种场景后续可能也不会有问题了。但是,从学习的角度而言,要注意他潜在的风险,及可能出现的问题,毕竟实际项目使用的jdk不是所谓的“高版本”
    回复 有任何疑惑可以回复我~ 2019-05-08 10:16:45
  • 提问者 慕神2874530 回复 Jimin #3
    老师,意思就是在没有任何同步手段的控制下,对普通共享变量而言,一个线程工作内存的值什么时候同步回主内存,其实是“不确定的”,也就可能会导致上面您提到的提前可见的问题,是吗?
    回复 有任何疑惑可以回复我~ 2019-05-08 10:43:55
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信