我正在用Java编写一个程序,它使用遗传算法将一组数字分成两组,因此它们的总和相等或尽可能接近相等。该程序应该在TextArea中附加每次迭代的结果。我不希望程序在进行计算时冻结,所以我将逻辑放在另一个线程中。更具体一点:
runner.valueProperty().addListener((observable, oldValue, newValue) -> {
resultsTA.appendText(newValue + "\n"); });
这是我的Runner类的call()方法中的主循环。在它之前,只有变量的声明。
while (!this.done) {
for (int i = 0; i < this.iterations; i++) {
current = this.population.get(i);
theBestOnes = current.getTheBestSpecimen();
bestCase = new BestSpecimen(i+1, theBestOnes);
this.updateValue(bestCase);
this.updateProgress(i, iterations);
next = Population.createParentPopulationFromExistingOne(current);
next.fillPopulation();
this.population.add(this.population.size(), next);
}
done = true;
}
return null;
顺便说一句,还有这个讨厌的null(参见上面的截图),它附加在TextArea上。我有什么想法可以摆脱它吗?返回语句在方法结束时重新生成,我无法返回没有内容的BestSpecimen对象。
答案 0 :(得分:0)
updateXXX
中的Task
方法正是他们所说的:他们更新了属性的值。更新在FX应用程序线程上执行。由于意图是提供可以在UI中观察到的值,如果在帧渲染之间多次改变值,则实际上仅使用帧的每次渲染时的最新值。这在documentation:
更新value属性。对updateValue的调用被合并并运行 稍后在FX应用程序线程上,所以调用updateValue,甚至来自 FX应用程序线程可能不一定立即导致 对此属性的更新,可以合并中间值 保存事件通知。
最简单的解决方案如下(尽管由于以下原因可能效果不佳:
while (!this.done) {
for (int i = 0; i < this.iterations; i++) {
current = this.population.get(i);
theBestOnes = current.getTheBestSpecimen();
bestCase = new BestSpecimen(i+1, theBestOnes);
final String text = bestCase.toString()"+\n";
Platform.runLater(() -> resultsTA.appendText(text));
this.updateProgress(i, iterations);
next = Population.createParentPopulationFromExistingOne(current);
next.fillPopulation();
this.population.add(this.population.size(), next);
}
done = true;
}
return null;
显然摆脱了valueProperty
的听众。
这里的问题是你最终可能会安排许多操作到FX应用程序线程,并可能最终淹没它的工作太多,以至于它无法执行重新绘制UI和处理用户事件的常规任务等等。
修复此问题有点棘手。一种选择是使用BlockingQueue<String>
对更新进行排队,使用AnimationTimer
更新用户界面:
BlockingQueue<String> textQueue = new LinkedBlockingQueue<>();
// ...
int iterations = ... ;
现在在你的任务中做
for (int i = 0; i < this.iterations; i++) {
current = this.population.get(i);
theBestOnes = current.getTheBestSpecimen();
bestCase = new BestSpecimen(i+1, theBestOnes);
final String text = bestCase.toString()+"\n";
textQueue.put(text);
this.updateProgress(i, iterations);
next = Population.createParentPopulationFromExistingOne(current);
next.fillPopulation();
this.population.add(this.population.size(), next);
}
return null;
当您开始任务时,也按以下方式启动AnimationTimer
:
AnimationTimer timer = new AnimationTimer() {
private int updates = 0 ;
@Override
public void handle(long now) {
List<String> newStrings = new ArrayList<>();
updates += textQueue.drainTo(newStrings);
StringBuilder sb = new StringBuilder();
newStrings.forEach(sb::append);
resultsTA.appendText(sb.toString());
if (updates >= iterations) {
stop();
}
}
};
timer.play();
即使这可能也不会很好,因为可能有大量的文本(以及大量的字符串连接来构建它)。您可以考虑使用ListView
而不是TextArea
,在这种情况下,您可以直接将队列排到列表视图的项目中。