首页 新闻 搜索 专区 学院

Java 并发和处理器乱序执行

0
悬赏园豆:20 [已解决问题] 解决于 2016-10-09 14:45

懒汉单例模式基础上,加上双检查锁机制,此时 “单例” 还需要加 volatile 关键字,这是为了消除处理器乱序执行的干扰吗?

如果是的话,谁能举出一个不加 volatile 关键字的情况下导致非单例的例子。(真实代码情景)

问题补充:

我换个方式描述问题吧,有人不理解我的问题。

双重检测在新的内存模型下能很好地工作吗?

private static Something instance = null;  
  
public Something getInstance() {  
  if (instance == null) {  
    synchronized (this) {  
      if (instance == null)  
        instance = new Something();//1  
    }  
  }  
  return instance;  
}  

此时可能得到一个不为空的但是还未初始化的对象。因为乱序写入引起的。

如果加上 volatile 就能解决这个问题。

这个理论我已经非常清楚了。

我想知道的是哪位大神能举个例子复现一下这个 不为空的但是还未初始化的对象。

JavaRecorder的主页 JavaRecorder | 初学一级 | 园豆:125
提问于:2016-09-18 17:28
< >
分享
最佳答案
0

这个跟单例没有关系吧,volatile是指示程序不要直接从寄存器中取值:

int a = 10; 

int b = a;

int c = a;

理论上来讲每次使用a的时候都应该从a的地址来读取变量值,但是这存在一个效率问题,就是每次使用a都要去内存中取变量值,然后再通过系统总线传到CPU处理,这样开销会很大。所以编译器会把a写进CPU寄存器里,像上面的代码,假如a在赋值期间没有被改变,就直接从CPU寄存器里取a的副本来进行赋值。但是bug也显而易见,当a在赋给b之后,可能a已经被另一个线程改变而重新写回了内存,但这个线程并不知道,依旧按照原来的计划从CPU寄存器里读a的副本进来赋值给c,就导致错误取值。

这时,就可以使用volatile来避免此类问题的发生,指示从变量地址取值,而不是CPU寄存器。

收获园豆:20
朝雨 | 菜鸟二级 |园豆:272 | 2016-09-18 18:37

这个理论我懂,怎样才能复现不用 volatile 关键字出现指令重排的情况。

JavaRecorder | 园豆:125 (初学一级) | 2016-09-18 18:38

@屌丝的烦恼: 这个不是很好重现,在特定条件下,编译器才会进行优化,而且要在release模式,跟cpu也有关系。

朝雨 | 园豆:272 (菜鸟二级) | 2016-09-18 21:21
其他回答(1)
0

    bool flag=true;
    long x=0;
    var td=new Thread(()=>{
    while(flag)
    {x++;}
    Console.WriteLine("this should not be shown");
    });
    td.Start();
    Thread.Sleep(1000);
    flag=false;
    Console.WriteLine("oops");
    Console.ReadLine();

 

java的不太会,你改下吧。

Daniel Cai | 园豆:10424 (专家六级) | 2016-09-19 09:44

您给的这是什么鬼?

支持(0) 反对(0) JavaRecorder | 园豆:125 (初学一级) | 2016-09-20 17:26

@屌丝的烦恼: volatile关键字的意义啊。那个flag加不加volatile结果不一样的。

支持(0) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2016-09-20 17:28

@Daniel Cai: 这个和乱序执行有关系吗?详细解释一下?

支持(0) 反对(0) JavaRecorder | 园豆:125 (初学一级) | 2016-09-20 17:37

@Daniel Cai: 网上一堆 volatile 禁止指令重排列的理论知识,但是找不到一个具体的例子对比。

支持(0) 反对(0) JavaRecorder | 园豆:125 (初学一级) | 2016-09-20 17:41

@屌丝的烦恼: 这个代码倒和乱序关系不大,唯一有点关联的就是volatile关键字了。如果没有这个关键字运行时可能会和代码执行的顺序不一致(忽略编译器优化)

但有了volatile关键字相当于有了full fence

类似

Thread.MemoryBarrier();

...

Thread.MemoryBarrier();

cpu在执行这样的逻辑只会按照代码中的线性逻辑去执行。

前面那个例子主要是说明volatile关键字会强制读取内存而不是寄存器中的数据。

支持(0) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2016-09-20 17:51

@Daniel Cai: 。。。。我需要的是:网上一堆 volatile 禁止指令重排列的理论知识,但是找不到一个具体的例子对比。这么个例子。

就像上楼说的,这样的例子不好重现。。。

支持(0) 反对(0) JavaRecorder | 园豆:125 (初学一级) | 2016-09-20 17:57

@屌丝的烦恼: http://stackoverflow.com/questions/2441279/volatile-guarantees-and-out-of-order-execution

支持(0) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2016-09-20 18:50

@Daniel Cai: 还是没有解决问题哦。都是理论都是理论。我所要的例子对比还是没人举出来。

支持(0) 反对(0) JavaRecorder | 园豆:125 (初学一级) | 2016-09-20 19:19

@屌丝的烦恼: 乱序是执行的时候,结果还是一样,你又没办法通过相同的结果看出来什么。

比如int i=1;

int j=2;

先执行后面再执行前面最后结果都是一样,你还能要什么例子。

支持(0) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2016-09-21 09:25

@Daniel Cai: 单线程的时候结果一样,多线程就不一定了。说了也挺多了。看一下问题补充,换种方式问。

支持(0) 反对(0) JavaRecorder | 园豆:125 (初学一级) | 2016-09-21 09:27

@屌丝的烦恼: 这个double check是跟jvm有关的吧,这个信息你最好去查下那个很有名的double check的文章。

支持(0) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2016-09-21 09:31
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册