指令重排序会破坏happens-before原则吗

2019-03-25 13:44|来源: 网路

今天阅读"深入理解java虚拟机"时,P333,关于happens-before解释,有这么一段:
引用
"时间上顺序与先行发生原则之间基本没有太大关系,所以我们衡量并发安全问题的时候不要受时间顺序的干扰,一切必须以先行发生原则为准。"

这段话我有疑问:指令重排序会破坏happens-before原则吗?happens-before的程序次序原则说:在一个线程内,按照程序代码顺序,书写在前面的操作会先行发生于书写在后面的操作。
如果线程内出现指令重排序,那不是破坏了程序次序原则了吗?是不是需要使用violate才能重新构建内存屏障呢?

问题补充:<div class="quote_title">hobitton 写道</div><div class="quote_div">就我的理解来说,happens-before不是原则,而是由良好同步的代码所带来的结论,不是所有的代码都会有happens-before的那几个结论,也就是说,happens-before是结论。因为命名的原因,这因果关系很容易搞混。 <br />同时,happens-before并不以时间作为衡量标准,happens-before的那几个结论才是衡量标准。 <br />不知道这样的解释你理解否,因为我之前有过同样的疑惑(http://www.iteye.com/problems/71027),但是这关系确实有些绕。</div> <br /><div class="quote_title">asyty 写道</div><div class="quote_div"><div class="quote_title">引用</div><div class="quote_div">"时间上顺序与先行发生原则之间基本没有太大关系,所以我们衡量并发安全问题的时候不要受时间顺序的干扰,一切必须以先行发生原则为准。"</div> <br /> <br />是这样的,它的意思不是说没有关系,是说在不影响happen-before的条件下,我们可以交换指令的顺序。 <br /> <br />也就是说,happen-before的原则还是要成立的,不能被破坏。 <br /> <br />比如有四条指令happen-before关系如下,a发生在b之前 c发生在d之前 <br /><pre name="code" class="java">A - &gt; B
C -&gt; D
</pre> <br />那么 以下的指令顺序都是正确的,因为都不违反happen-before的条件 <br /><pre name="code" class="java">a,b,c,d
a,c,b,d
c,a,b,d
c,d,a,b
</pre> <br /> <br /></div> <br />我的理解是,遵循happens-before法则,所以才能有良好的同步。 happens-before是因,良好同步是果。有确定结论吗?

问题补充:<div class="quote_title">asyty 写道</div><div class="quote_div"><div class="quote_title">引用</div><div class="quote_div">"时间上顺序与先行发生原则之间基本没有太大关系,所以我们衡量并发安全问题的时候不要受时间顺序的干扰,一切必须以先行发生原则为准。"</div> <br /> <br />是这样的,它的意思不是说没有关系,是说在不影响happen-before的条件下,我们可以交换指令的顺序。 <br /> <br />也就是说,happen-before的原则还是要成立的,不能被破坏。 <br /> <br />比如有四条指令happen-before关系如下,a发生在b之前 c发生在d之前 <br /><pre name="code" class="java">A - &gt; B
C -&gt; D
</pre> <br />那么 以下的指令顺序都是正确的,因为都不违反happen-before的条件 <br /><pre name="code" class="java">a,b,c,d
a,c,b,d
c,a,b,d
c,d,a,b
</pre> <br /> <br /></div> <br />书中在解释volatile时,引入这样一个例子: <br />线程A: <br />readConfig(); //读取配置 <br />init=true; <br /> <br />线程B: <br />while(init){ <br />&nbsp; useConfig(); //使用配置 <br />} <br /> <br />由于线程A可能会发生指令重排序,所以线程B使用的配置可能尚未加载,所以使用volatile解决此问题。 可见线程A发生的指令重排序破坏了happens-before的程序次序原则,这个怎么理解? <br />

问题补充:<div class="quote_title">asyty 写道</div><div class="quote_div"><div class="quote_title">引用</div><div class="quote_div">线程A: <br />readConfig(); //读取配置 <br />init=true; <br /> <br />线程B: <br />while(init){ <br />&nbsp; useConfig(); //使用配置 <br />} <br /> <br />由于线程A可能会发生指令重排序,所以线程B使用的配置可能尚未加载,所以使用volatile解决此问题。 </div> <br />比如说,在readConfig();里有N多的指令要执行 <br />指令 <br />a <br />b <br />c <br />d <br />init=true; <br /> <br />如果abcd和init变量都没有关系,就是不存在happens-before关系的话,若果被重排,比如说可能变成 <br />a <br />b <br />init=true; <br />c <br />d <br /> <br />此时其实c,d还没有执行 但是b线程里init=true;已经成立了。。。所以就执行 useConfig(); 了 然后会出错 <br /></div> <br />也就是说,指令只关注线程内部指令abcd和变量init是否存在happens-before关系,不会考虑对其他线程的影响,所以指令重排序并没有破坏线程内部的happens-before法则,但是可能会影响其他线程,所以我们使用volatile来保证顺序。 这样理解是否正确?

问题补充:<div class="quote_title">asyty 写道</div><div class="quote_div"><div class="quote_title">引用</div><div class="quote_div">也就是说,指令只关注线程内部指令abcd和变量init是否存在happens-before关系,不会考虑对其他线程的影响</div> <br />可以这么认为吧,一般来说每个线程都是有一块独立的内存区域,把共享的变量存一个副本进行操作,对其他线程的顺序是不可见的,使用了volatile相当于禁止了在线程内部内存或者是寄存器操作共享数据,而是直接在共享的主存上进行,相当于每个线程能看见别的线程对主存的操作,所以就能够保证读写顺序 <br /> <br />一般指令顺序的优化都是编译器和cpu干的事情</div> <br />明白,谢谢指导,也谢谢hobitton和zhxinig的回答。

相关问答

更多
  • 我稍微修改了您的代码以包含对最后一行的引用 Sub sort_s() Range("A19").Select Range(Selection, Selection.End(xlToRight)).Select Range(Selection, Selection.End(xlDown)).Select Range(Selection, Selection.End(xlDown)).Select ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clea ...
  • 但是,我希望我的前4个结果不按其排名(voting.vote)的顺序出现,而是按其发布日期(entries.id)的顺序排列。 尝试这个: SELECT * FROM ( SELECT entries.permalink, entries.title, voting.vote FROM entries LEFT JOIN voting ON voting.id = entries.id WHERE date>='$twoweek ...
  • Lodash的_.sortBy()不能选择降序或升序。 使用_.orderBy()代替( 沙箱 ): sort(key) { const columnState = !this.state[`toggle-${key}`]; this.setState({ [`toggle-${key}`]: columnState, data: orderBy( this.state.data, [key], columnState ? 'desc' : 'a ...
  • 只需按值排序,具体取决于哪个属性包含短语: var orderedResult = result.Where(x => x.Title == phrase || x.Synopsis == phrase) .OrderBy(x => x.Title == phrase ? 1 : 2); 如果要应用更多排序条件,可以使用ThenBy : var orderedResult = result.Where(x => x.Title == phrase || ...
  • 是的,你是对的,发生在独立数据发生变化之前的关系之前。 至于其他示例,请不要忘记在发生之前 - 也适用于同一线程中的事件。 规则很简单: 如果x和y是同一个线程的动作,并且x在程序顺序中出现在y之前,那么hb(x,y) 。 所以Java方法中的每个语句都发生在每个后面的语句之前,但当然JIT编译器和CPU可以自由地重新排序独立语句(并且它们实际上经常这样做以优化性能)。 有时你可以从另一个没有发生过的线程中观察到这个重新排序 - 在与当前线程的关系之前。 Yes, you're right, happens ...
  • 我的理解是: 你是对的 他们可以重新排序,但只有在行动B不取决于行动A的结果 事前 - 事前关系并没有说明重新排序行为。 它只是说如果HB(A,B)成立,那么动作B必须看到动作A的记忆效应。 如果行动B不使用行动A的任何结果,那么他们没有理由不能重新排序。 (通常,“使用任何其他操作的结果”都非常广泛,只能用于非常简单的操作(如内存读取/写入),而不能用于使用外部资源(如I / O操作)的操作或基于时间的操作) It is my understanding that: you're right they c ...
  • 因为A和B之间没有发生过关系 错误。 [intro.multithread] / P14: 评估A 发生在评估B 之前 ,如果: A在B之前排序,或 线程发生在B之前。 since there is no happens-before relationship between A and B Wrong. [intro.multithread]/p14: An evaluation A happens before an evaluation B if: A is sequenced before B, o ...
  • 通常你应该能够把你的请求参数变成一个数组,如: @RequestParam(value = "orderBy", required = false, defaultValue = "severity") String[] orderBy, @RequestParam(value = "order", required = false, defaultValue = "desc") String[] order, Usually you should be able to just turn your re ...
  • 这适用于数字和类似的数据类型 sorted(pairs, key=lambda x: (x[0], -x[1])) 这将仅适用于Python 2中的所有类似类型:( sorted(pairs, lambda x, y: cmp(x[0], y[0]) or cmp(y[1], x[1])) This will work for numbers and similar data types sorted(pairs, key=lambda x: (x[0], -x[1])) This will work ...
  • 将x[1]转换为int(x[1]) : sorted(d,key=lambda x:(int(x[0]), int(x[1]))) 输出: [['1', '1', '3'], ['1', '4', '9'], ['1', '7', '14'], ['1', '12', '3'], ['2', '3', '1']] Convert x[1] to int(x[1]): sorted(d,key=lambda x:(int(x[0]), int(x[1]))) Output: [['1', '1', '3 ...