Java 从SolverEventListener修改解决方案时的Optaplanner ConcurrentModificationException

Java 从SolverEventListener修改解决方案时的Optaplanner ConcurrentModificationException,java,optaplanner,Java,Optaplanner,我正在使用集成到JavaFXGUI中的OptaPlanner解算器解决一个调度问题,该GUI会在每次改进时更新。 由于将其连接到GUI,此异常经常在构造完成后发生 Exception in thread "Thread-6" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.Arra

我正在使用集成到JavaFXGUI中的OptaPlanner解算器解决一个调度问题,该GUI会在每次改进时更新。 由于将其连接到GUI,此异常经常在构造完成后发生

Exception in thread "Thread-6" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner$FieldAccessingSolutionClonerRun.cloneCollection(FieldAccessingSolutionCloner.java:297)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner$FieldAccessingSolutionClonerRun.process(FieldAccessingSolutionCloner.java:280)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner$FieldAccessingSolutionClonerRun.processQueue(FieldAccessingSolutionCloner.java:273)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner$FieldAccessingSolutionClonerRun.cloneSolution(FieldAccessingSolutionCloner.java:206)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner.cloneSolution(FieldAccessingSolutionCloner.java:72)
    at org.optaplanner.core.impl.score.director.AbstractScoreDirector.cloneSolution(AbstractScoreDirector.java:142)
    at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution(DefaultSolverScope.java:198)
    at org.optaplanner.core.impl.solver.DefaultSolver.runPhases(DefaultSolver.java:217)
    at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:176)
    at no.scheduling.shifts.solver.ShiftOptaPlanner.solve(ShiftOptaPlanner.java:124)
    at application.ScheduleSolver.run(ScheduleSolver.java:69)
    at java.lang.Thread.run(Thread.java:745) 
我的GUI代码确实修改了收到的解决方案,因为它包含的项目必须以排序方式显示给用户。 当我对侦听器的
bestSolutionChanged()
方法提供的解决方案使用防御性克隆时,问题就消失了。 我是否在这里做错了什么,或者事件侦听器有时可能会获取OptaPlanner使用的有线实例,而不是防御克隆? 我真的希望避免克隆步骤,因为我目前可用的克隆功能有限且速度缓慢

编辑:通过进一步调试,我想我能够在OptaPlanner源代码(6.4.0最终版本)中找到问题所在 完成构造启发式阶段后,
BestSolutionRecaller
中的
updateBestSolution()
方法将存储在
solverScope
中的相同解决方案实例放入
fireBestSolutionChanged()
中:

 public void updateBestSolution(DefaultSolverScope solverScope, Solution solution, int uninitializedVariableCount) {
        (...)
        solverScope.setBestSolution(solution);
        (...)
        solverEventSupport.fireBestSolutionChanged(solution, uninitializedVariableCount);
    } 
然后,在我的GUI线程对其中的事件列表进行排序的同时,这个完全相同的实例在
setWorkingSolutionFromBestSolution
中进行克隆,并且在
FieldAccessingSolutionClonerRun.cloneCollection()中迭代事件列表时引发异常。
我猜问题的解决方案是克隆到
fireBestSolutionChanged()
的解决方案

我是无意中发现了一个微妙的错误还是我做错了什么?谢谢

不要在bestSolutionEvent期间进行深度克隆(=防御复制)。由于性能原因,optaplanner core也没有这样做(代码中没有bug,但planner在BestSolutionRecaller中做了一个规划克隆(!=深度克隆)

相反,在
ProblemFactChange
s中进行深度克隆

深度克隆不同于计划克隆(文档中对此进行了解释)。7.0.0.Beta2文件对此进行了解释,以进一步澄清:


ConcurrentModificationException
与1)从一个线程修改一个对象,同时对它和另一个线程执行操作有关