【问题标题】:Synchronized and Code reorder in javajava中的同步和代码重新排序
【发布时间】:2013-09-21 06:06:10
【问题描述】:

在某些链接中给出了同步块内的代码重新排序是可能的,而其他一些站点则说不可能。你能举个例子来描述一下使用同步时代码重新排序的实际情况吗?

【问题讨论】:

    标签: java multithreading jvm synchronized java-memory-model


    【解决方案1】:

    有两种可能的重新排序:

    • 同步块之前或之后的语句可以在同步块内移动(当然前提是它们不在不同的同步块中)-有时称为the "roach motel" principle
    • 同步块内的语句可以重新排序,只要修改是顺序一致的(即,与块共享发生前关系的其他代码看不到效果)

    但更重要的是:如果您的代码正确同步,这些重新排序不会影响程序的执行,这是您应该关心的全部。

    示例(所有变量最初为 0):

    线程1:

    a=1;
    synchronized(lock) {
        b=1;
        c=1;
    }
    d=1;
    

    线程2:

    synchronized(lock) {
        if (a==1) print(b); // can print 0 or 1
        if (b==1) print(a)/print(c); // always prints 1
        if (b==1) print(d); // can print 0 or 1
        if (d==1) print(a)/print(b)/print(c); // always prints 1
    }
    

    特别是,允许​​将 d=1 移动到 c=1 之前或将 a=1 移动到 b=1 之后,因为线程 2 无法观察到,因为线程 1 执行的同步块看起来就像来自线程 2 的原子操作。

    另一方面,不使用相同锁的线程将能够观察到这些重新排序。

    【讨论】:

    • 感谢您的回答。但是假设我在同步块之后有一个代码。根据第一个规范,该代码可以放在同步块中。再次根据同步块内的第二个规范代码可以重新排序,直到在不违反关系之前发生。那么编译器是否有时可以重新排序同步内的while代码块以及出现在该块之后并在同步块内获取的代码?
    • 不确定我理解你在说什么 while 块 - 但理论上是的,编译器可以在同步块之后将代码重新排序到同步块中的某些语句之前(但不是在块之前) .
    【解决方案2】:

    运行时可能会重新排序操作,只要尊重发生之前。规范writes:

    应该注意的是,两个动作之间存在发生之前的关系并不一定意味着它们必须在实现中以该顺序发生。如果重新排序产生与合法执行一致的结果,则不是非法的。

    更具体地说,如果两个操作共享一个happens-before 关系,那么对于它们不共享happens-before 关系的任何代码,它们不一定必须以该顺序发生。例如,一个线程中的写入与另一个线程中的读取处于数据竞争中,这些读取可能会出现乱序。

    特别是,如果一个线程这样做:

    synchronized (lock) {
        x = 1;
        y = 1;
    }
    

    还有一个:

    if (y == 1) System.out.println(x);
    

    这些线程之间没有happens-before关系,另一个线程可能会观察到yx之前被分配,并打印0。

    【讨论】:

      猜你喜欢
      • 2019-03-19
      • 1970-01-01
      • 2021-09-28
      • 2013-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-02
      • 1970-01-01
      相关资源
      最近更新 更多