JavaFX-从另一个线程更新TextArea并缺少迭代

JavaFX-从另一个线程更新TextArea并缺少迭代,java,multithreading,javafx,Java,Multithreading,Javafx,我正在用Java编写一个程序,它使用遗传算法将一组数字分成两组,使它们的和等于或尽可能接近等于。程序应该将每次迭代的结果附加到TextArea中。我不希望程序在进行计算时冻结,所以我将逻辑放在另一个线程中。更具体地说: 我创建了类Runner,它扩展了任务。BestSample是我的类,它在这里用于打印迭代编号和在此迭代中找到的最佳解决方案列表。它还更新了ProgressBas,但并不重要。我通过在我的控制器类中添加以下行来实现:runnerProgressPB.progressProperty

我正在用Java编写一个程序,它使用遗传算法将一组数字分成两组,使它们的和等于或尽可能接近等于。程序应该将每次迭代的结果附加到TextArea中。我不希望程序在进行计算时冻结,所以我将逻辑放在另一个线程中。更具体地说:

  • 我创建了类Runner,它扩展了任务。BestSample是我的类,它在这里用于打印迭代编号和在此迭代中找到的最佳解决方案列表。它还更新了ProgressBas,但并不重要。我通过在我的控制器类中添加以下行来实现:runnerProgressPB.progressProperty().bind(runner.progressProperty())
  • 我想用同样的方法更新TextArea,但它没有正常工作。关键是它用当前迭代更新了TextArea,但删除了以前迭代的结果
  • 我使用了不同的方法:在我的控制器类中,我添加了以下代码:
    runner.valueProperty().addListener((可观察、旧值、新值)->{
    resultsTA.appendText(newValue+“\n”);})
  • 很简单,它只是监听名为runner的线程中ValueProperty的更改。在创建迭代过程中发生的BestTestObjor对象之后,我更新ValueFipe属性。然而,问题来了——TextArea缺少许多迭代。看一下屏幕截图。有超过100个迭代在一个点上丢失!
  • 那么,我认为发生了什么?线程运行速度太快,TextArea无法及时更新信息。我在for循环中加入了一些Thread.sleep(),这减少了缺少迭代的次数,但程序运行速度非常慢!我使用了Thread.sleep(20),花了5-10分钟才完成,仍然缺少一些迭代。这不是解决办法
  • 我想要实现什么?我希望运行线程在设置ValueProperty后挂起,并等待TextArea未更新。我想我应该使用wait()/notify()结构,但不知道具体在哪里。我在线程方面是个新手
  • 下面是我的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;
    
    while(!this.done){
    for(int i=0;i
  • 顺便说一句,还有这个讨厌的空值(见上面的屏幕截图),它被附加到TextArea。有没有办法摆脱它?方法末尾需要return语句,我不能返回没有内容的BestSample对象


  • Task
    中的
    updateXXX
    方法完全按照它们说的做:它们更新属性的值。更新在FX应用程序线程上执行。由于目的是提供一个可以在UI中观察到的值,如果在帧渲染之间多次更改该值,则实际上只会使用帧每次渲染时的最新值。这在以下文件中明确说明:

    更新“值”属性。对updateValue的调用被合并并运行 稍后在FX应用程序线程上,甚至从 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;
    
    现在你的任务是什么

    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;
    

    即使这样,它的性能也可能不太好,因为可能有大量的文本(以及大量的字符串连接来构建它)您可以考虑使用<代码> ListView < /C>而不是<代码> TeTaRea<代码>,在这种情况下,您可以将队列直接排到列表视图的项。

    < P> <代码> UpDeXXXX < /Case>方法> <代码>任务>代码>按照它们所说的:它们更新属性的值。其目的是提供一个可以在UI中观察到的值,如果该值在帧渲染之间多次更改,则实际上只会使用帧每次渲染时的最新值。这在:

    更新value属性。合并并运行对updateValue的调用 稍后在FX应用程序线程上,甚至从 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;
    
    现在你的任务是什么

    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;
    

    这可能不太好,因为可能有大量的文本(和大量的字符串连接来构建它)。您可以考虑使用<代码> ListView < /C> >而不是<代码> TetraRea<代码>,在这种情况下,您可以将队列直接排到列表视图的项。

    所有<代码> UpDATEXXX(…)
    方法旨在设置属性的值,而不是提供值流。因此,它们可能会将多个调用合并为一个调用(特别是,如果在两个相邻帧渲染之间多次更新值,则可能会发生这种情况)。您需要显式附加到FX应用程序线程上的文本区域。(请注意,如果您的输出很大,则文本区域可能不是最佳选项。)我如何执行此操作?我运行我的计算