我的方法看起来像是由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)。“
谢谢你!答案 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。