Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
什么是Java synchronized的有效重新排序?_Java_Multithreading - Fatal编程技术网

什么是Java synchronized的有效重新排序?

什么是Java synchronized的有效重新排序?,java,multithreading,Java,Multithreading,许多人问了类似的问题,但没有一个答案让我满意。 我非常确定的唯一两条重新排序规则如下 如下: 同步块内的操作(或仅调用它 “关键部分”(critical section)允许重新排序,只要它符合“仿佛”序列语义 业务(包括两者) 不允许将读操作和写操作移出(重新排序)关键部分 但是,对于同步块之前或之后的操作,是否可以将其移动到关键部分?对于这个问题,我发现了一些相反的观点。例如,报告称编译器将在MonitorEnter之后和MonitorExit之前插入一些屏障: MonitorEnter

许多人问了类似的问题,但没有一个答案让我满意。 我非常确定的唯一两条重新排序规则如下 如下:

  • 同步块内的操作(或仅调用它 “关键部分”(critical section)允许重新排序,只要它符合“仿佛”序列语义
  • 业务(包括两者) 不允许将读操作和写操作移出(重新排序)关键部分
  • 但是,对于同步块之前或之后的操作,是否可以将其移动到关键部分?对于这个问题,我发现了一些相反的观点。例如,报告称编译器将在MonitorEnter之后和MonitorExit之前插入一些屏障:

    MonitorEnter
     (any other needed instructions go here )
    [LoadLoad] <===MB1:Inserted memory barrier
    [LoadStore] <===MB2:Inserted memory barrier
    (Begin of critical section)
    
    ....
    (end of critical section)
    [LoadStore] <===MB3:Inserted memory barrier
    [StoreStore] <===MB4:Inserted memory barrier
     (any other needed instructions go here )
    MonitorExit
    
    允许产生以下代码的重新排序:

    根据理解1,“y=1”不能在临界段内上移,
    所以我很困惑,哪一个是正确的和完整的?

    重新排序不关心内存障碍。即使编译器总是在任意两条指令之间插入最强的内存屏障,这些重新排序仍然是允许的

    现在,给定一个指令序列,可能在从原始序列重新排序之后,编译器需要在一些指令之间插入适当的内存屏障

    例如,给定一个原始指令序列

    volatile store x
    normal   store y
    
    这两条指令之间不需要内存屏障

    但是,编译器可以选择将其重新排序为

    normal   store y
    volatile store x
    
    然后,两个指令之间需要一个StoreStore屏障。CPU只有一条“存储”指令,没有正常/易失性存储的概念。CPU可能有无序的存储。Java语义要求另一个CPU在volatile
    storey
    的影响之前不能观察到
    storex
    的影响;因此,StoreStore用于告诉CPU按顺序存储它们

    (如果编译器足够聪明,它会记住原始程序不需要
    y->x
    的顺序,因此实际上不需要这个障碍。但假设编译器没有那么聪明。)


    罗奇模型汽车旅馆--

    JMM的要点是在不同线程上的指令之间建立一些(部分)顺序,以便可以定义读/写的效果。在下面的示例中

    thread 1             thread 2
    
      a1                   a2
      |
     }b1       ----->      b2{
                           |
      c1                   c2
    
    建立同步顺序
    b1->b2
    ,可以是
    volatile store->volatile load
    ,或者
    监视器退出->监视器进入
    。这将在订单发生之前连接
    a1->b1->b2->c2

    由于我们需要保证
    a1->c2
    的顺序,因此
    a1
    不得使用
    b1
    重新排序,
    c2
    不得使用
    b2
    重新排序;也就是说,蟑螂不能“退房”


    另一方面,JMM希望尽可能的软弱;它没有说明
    c1
    a2、b2、c2
    之间的影响;因此,
    c1
    可以使用
    b1
    自由重新排序。类似地,
    a2
    可以使用
    b2
    重新排序。也就是说,蟑螂可以“签入”。

    即使“编译器足够聪明”,根据我的理解,编译器仍然需要在您的示例中插入Store,因为它可以防止处理器在volatile存储之前的指令和volatile存储本身之间重新排序,从而强制执行volatile存储的语义。感谢@bayou.io的详细解释!总之,我的理解是,对于MonitorEnter之前或MonitorExit之后的读写操作,可以在编译(JIT编译)期间将其移动(重新排序)到关键部分。但是,一旦编译完成,处理器就无法将移动的读写操作移出,由于编译器插入的内存障碍会被处理器所重视,对吗?假设原始程序是
    normalstorea;易失性存储器B
    。JMM语义是,在另一个线程看到B的效果之后,它也应该看到A的效果。现在,JVM的工作是在特定的CPU体系结构上实现该语义。默认情况下,CPU可能会无序执行写操作;但它也支持一个屏障指令来禁用它。因此,在CPU上,JVM首先将保持A和B的顺序,即JVM不会重新排序;然后JVM将在A和B之间插入StoreStore,也就是说CPU不会重新排序。如果指令
    P;Q
    不应按照JMM重新排序,第一个表将禁止JVM重新排序,第二个表将禁止CPU重新排序。综合效应为
    P;Q
    当其他CPU观察到时,似乎不会重新排序。
    volatile store x
    normal   store y
    
    normal   store y
    volatile store x
    
    thread 1             thread 2
    
      a1                   a2
      |
     }b1       ----->      b2{
                           |
      c1                   c2