防止Java中的JIT重新排序

时间:2013-10-29 04:19:07

标签: java multithreading jit

我的方法看起来像是由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容器是“this”的一个字段,由someThread访问。当我通过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)。“

谢谢你!

2 个答案:

答案 0 :(得分:3)

你绝对正确的是你创造了一个先发生过的关系。 一般来说

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

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

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

不会重新订购。这正是你在纠正中所做的。

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

答案 1 :(得分:1)

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