对不起,这不是SSCCE,但我试图在这个问题上充分描述对象。
嵌套在里面的所有内容都是成员变量
PerformanceData (Object)
|- process (String): The name of the process this PerformanceData has data for (ie: firefox.exe)
|- metrics (Map<String, List<DataValue>>): The keys are metrics whose usage we are monitoring ("cpu", "disk", "memory")
DataValue (Object)
|- value (double): The observed value (ie: 0.43 which means 43% cpu usage)
|- time (Date) : The time the value was observed
ModelEnsemble (Thread)
|- data (List<DataValue>): The DataValues available to this ModelEnsemble
|- models (Map<String, IEnsembleModel>): maps a name to each IEnsembleModel.
||-This is used because the user can choose which IEnsembleModels to run via a myApp.properties file.
||-In the constructor, we parse this properties file for enabled IEnsembleModels
IEnsembleModel (Object)
|- window (int): The max size input should be. When input.size() > window, we remove (older elements) from the front of the queue
|- input (ArrayDeque<DataValue>): The queue of DataValues we are modelling
|- addInput (DataValue): adds a DataValue to the end of the list
|- getLastInput (DataValue): The last inserted DataValue (from addInput)
|- getLastPrediction (double): The second latest predicted outcome from the current input data (minus the last input)
|- getError (double): Percent error, how much the last prediction (getLastPrediction) differed from the last input (getLastInput)
|- getNextPrediction (double): The latest predicted outcome from the current input data (including the last input)
|- model (double): Computes the next prediction, returns getNextPrediction().
以下代码抛出ConcurrentModificationException。我相信它是由匿名的PropertChangeListener以及addInput()引起的。一些慷慨的SO用户可以查看我的代码并指出我允许多个编辑或竞争条件的位置吗?
for (final PerformanceData perfData : myPerformanceData) {
for (Map.Entry<String, List<DataValue>> entry : perfData.getMetrics().entrySet()) {
final String metric = entry.getKey();
final List<DataValue> values = entry.getValue();
ensemble = new ModelEnsemble(values);
// Pretty sure this causes the Concurrent Modification Exception
ensemble.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
IEnsembleModel model = (IEnsembleModel) evt.getNewValue();
ModelPanel.this.firePropertyChange("result", null, new ModelResult(
perfData.getProcess(),
metric,
model.getLastInput().getValue(),
model.getLastPrediction(),
model.getError()
));
}
});
ensemble.start();
}
}
int offset = 0; // array index we are currently modelling
while (!this.isInterrupted()) {
if (offset > data.size() - 1) {
// we've exhausted the input input, close the model solver
System.out.println("Input input exhausted, ending ensemble");
this.interrupt();
} else {
// get the latest input value
DataValue value = data.get(offset);
for (Map.Entry<String, IEnsembleModel> entry : models.entrySet()) {
String name = entry.getKey();
IEnsembleModel model= entry.getValue();
model.addInput(value);
model.model();
this.notifyListeners(name, model); // triggers that anonymous PropertyChangeListener in the above piece of code
}
}
offset++; // so we can model the next element of the DataValue List
}
double model() {
double sum = 0;
for (DataValue value : input) {
sum += value.getValue();
}
setNextPrediction(sum / ((double) input.size()));
return getNextPrediction();
}
void addInput(DataValue input) {
this.input.addLast(input); // add to back of queue
this.maintainWindow();
}
private void maintainWindow() {
int size = this.input.size();
while (size > window) { // by default window = 10
this.input.pop(); // error here
}
}
这发生在每个线程中:
Exception in thread "Thread-13" java.util.ConcurrentModificationException
at java.util.ArrayDeque$DeqIterator.next(ArrayDeque.java:632)
at ca.yorku.cirillom.ensemble.models.MovingAverageModel.maintainWindow(MovingAverageModel.java:93)
at ca.yorku.cirillom.ensemble.models.MovingAverageModel.addInput(MovingAverageModel.java:53)
at ca.yorku.cirillom.ensemble.models.ModelEnsemble.run(ModelEnsemble.java:123)
这也发生在每个线程中:
Exception in thread "Thread-7" java.util.NoSuchElementException
at java.util.ArrayDeque.removeFirst(ArrayDeque.java:278)
at java.util.ArrayDeque.pop(ArrayDeque.java:507)
at ca.yorku.cirillom.ensemble.models.MovingAverageModel.maintainWindow(MovingAverageModel.java:85)
at ca.yorku.cirillom.ensemble.models.MovingAverageModel.addInput(MovingAverageModel.java:49)
at ca.yorku.cirillom.ensemble.models.ModelEnsemble.run(ModelEnsemble.java:123)
我觉得某处必须有竞争条件,因为maintainWindow()检查input.size()
是否大于window
,默认为10。如果NoSuchElementException
大于10
size()
不会发生
答案 0 :(得分:3)
只有在以下情况下才会抛出并发修改异常:
这两行正在进行迭代,因此在这些for循环退出之前,myPerformanceData或perfData.getMetrics正在被修改。
for (final PerformanceData perfData : myPerformanceData) {
for (Map.Entry<String, List<DataValue>> entry : perfData.getMetrics().entrySet()) {
或者这一行:
for (Map.Entry<String, IEnsembleModel> entry : models.entrySet()) {
错误的完整堆栈跟踪应该为您提供并发修改发生的位置,并让您识别正在修改的集合。然后,您只需要修改逻辑,以便在迭代它们之外发生对集合的更改。