2

我有一个看起来像是由 JIT 重新排序的方法。

public boolean finishRequest(boolean success) {
    if (success) {
        error = false;
        wayPoints.put(FINISH_SUCCESS_WP, System.currentTimeMillis());
    } else {
        wayPoints.put(FINISH_ERROR_WP, System.currentTimeMillis());
        error = true;
    }
    someThread.addToQueue(this);//...submit for further processing on separate thread
    return true;
}

wayPoints 容器是由 someThread 访问的“this”字段。当它通过 getWayPoints() 遍历这个 wayPoints 时,我最终在 someThread 中的某行代码中得到了 ConcurrentModificationException。

为了解决这个问题,我创建了一个新的不可变类,它将被传递给 someThread 而不是“this”。

public boolean finishRequest(boolean success) {
    if (success) {
        error = false;
        wayPoints.put(FINISH_SUCCESS_WP, System.currentTimeMillis());
    } else {
        wayPoints.put(FINISH_ERROR_WP, System.currentTimeMillis());
        error = true;
    }
    PLog plog = new PLog(wayPoints.toArray(new WayPoint[wayPoints.size()]));
    someThread.addToQueue(plog);//...submit for further processing on separate thread
    return true;
}

在第一段代码中,JIT 可以重新排序 'someThread.addToQueue()' 和 'wayPoints.put()' 因为编译器可以告诉 wayPoints 没有被进一步读取到当前线程中。但是在固定代码中,由于我使用 wayPoints 来构造我的新 PLog 实例,我现在是否有效地阻止了 JIT 重新排序?

参考...... http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5

从 JLS 的第 17.4.5 节的阅读中,我认为我已经创建了一个具有法律约束力的发生之前,这将阻止重新排序,因为我现在在写入之后从 wayPoints 读取......“如果 x 和 y 是动作在同一线程中,x 在程序顺序中位于 y 之前,然后是 hb(x, y)。”

谢谢你!

4

2 回答 2

3

您是绝对正确的,您已经创建了先发生的关系。一般来说

一个 = X ; ( X 是一些不涉及 a & b 的常数或表达式) b = Y ;( Y 是一些不涉及 a & b 的常数或表达式)

以上两个可以由 JIT 重新排序

但是 a = X ; b = a * Y ;

不会重新排序。这几乎就是您在更正中所做的。

解决问题的其他方法是声明 wayPoints volatile。如果您将 wayPoints 声明为 volatile,则写入 volatile ( wayPoints) 不会与您原始程序顺序中的其他内存操作 ( someThread.addToQueue() ) 一起重新排序。

于 2013-10-29T05:24:12.963 回答
1

addToQueue 和相应的“removeFromQueue”有什么作用?他们将需要某种同步,在这种情况下,方法“finishRequest”的确切功能无关紧要。您重新排列的方法与以前没有或多或少的错误,当然,除非“this”在 finishRequest 运行之前已经在队列中。如果 addToQueue 不与其对应的同步, someThread 仍然可以看到一个不完整构造的 PLog。

于 2013-10-29T07:49:36.307 回答