【问题标题】:Using two threads causes JavaFX to crash [duplicate]使用两个线程会导致 JavaFX 崩溃 [重复]
【发布时间】:2021-07-23 10:42:43
【问题描述】:

我需要使用两个线程将两个不同的字符附加到同一个 JavaFX TextArea。我可以让一个工作,但是当我添加第二个线程时,它会因一些很长的异常而中断。我做错了什么?

我查看了这个问题以寻求指导,它让我有一个线程工作,但不是两个: Display output of two different Threads in two separate JavaFx TextArea's

package application;
    

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;



public class Main extends Application {
    
    
    @Override
    public void start(Stage primaryStage) {
        
        try {
            BorderPane root = new BorderPane();
            TextArea textArea = new TextArea();
            textArea.setWrapText(true);
            root.setCenter(textArea);
            textArea.setText("");
            
            new Thread(() -> PrintChar(textArea, 'a', 100)).start();
            new Thread(() -> PrintChar(textArea, 'b', 100)).start();
                    
            
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
    
    private void PrintChar(TextArea textArea, char letter, int numb)
    {
        for(int i = 0; i < numb; ++i)
        {
            textArea.appendText(letter + "");
        }
    }
}

【问题讨论】:

  • UI 由单线程控制,不是线程安全的。要从您需要使用Platform.runLater(() -&gt; {textArea.appendText(letter + "");}); 的任何其他线程编辑 UI,请注意如何将从另一个线程更新 UI 的代码放在括号内。如果您不这样做,那么如果不同的线程尝试同时与同一事物交互,您将遇到各种问题。通过使用 runLater,更新被安排为一次一个,因为 UI 线程能够管理它们,同时还管理用户交互。
  • @sorfiend,感谢您的回复。问题是懒惰的老师从不同版本的教科书中给出了这个作业。学生所拥有的不包括多线程。她发布了她想要做的事情,并说应该可以使用她在 20 分钟视频中介绍的工具来完成。视频中没有你说的内容:(
  • 我查看了其他线程,但它并没有很好地回答我的问题。同步如何与它一起工作?
  • 同步是一个完全不同的主题,而不是您在场景中需要的东西,因为 UI 线程会正确管理它。当多个线程访问相同的数据时,同步很重要,但是使用Platform.runLater 可以为 javafx 解决这个问题。对于非 UI 相关的代码,它需要一种完全不同的方法,一种方法是谨慎使用 volatile 变量和事件队列。我强烈推荐关注官方教程,它有一些很棒的信息:docs.oracle.com/javase/tutorial/essential/concurrency/…
  • 为什么你认为你需要并发?这是一项很可能可以使用单线程完成的学校作业,尤其是因为你还没有学过并发

标签: java multithreading javafx


【解决方案1】:

您不需要为此任务使用两个线程。您可以简单地按顺序调用 PrintChar 方法:

PrintChar(textArea, 'a', 100);
PrintChar(textArea, 'b', 100);

使用这将导致 textArea 输出如下所示,每个输出 100 个:

aaaaaa.....bbbbbb.....


但是,如果您“想”像这样使用两个线程:

new Thread(() -> PrintChar(textArea, 'a', 100)).start();
new Thread(() -> PrintChar(textArea, 'b', 100)).start();

然后您可以通过使用Platform.runLater 来安排 UI 更改,例如围绕与 javafx 组件交互的代码,如下所示:

Platform.runLater(() -> {
    textArea.appendText(letter + "");
});

这将导致 textArea 被两个线程混合更改,看起来像这样(如果不采取额外步骤,您无法控制执行顺序):

bbaaabbbbbbbbbaaaaa.....


如果您想使用线程,但也使用同步,那么这与完全不使用线程没有什么区别(至少在您的场景中),但是,您可以这样做:

private synchronized void PrintChar(TextArea textArea, char letter, int numb)
{
    for(int i = 0; i < numb; ++i)
    {
        //We still need to wrap the UI code, because other methods from other threads could still interact with the UI and cause issues
        Platform.runLater(() -> {
            textArea.appendText(letter + "");
        });
    }
}

输出看起来像这样,每个顺序有 100 个:

aaaaaa.....bbbbbb.....

【讨论】:

  • 因此,每次调用 runLater 时,OP 都很清楚,这是另一个将要执行的线程。所以这回答了你的问题。您不需要同步,因为这是在 JavaFX 中进行同步的唯一方法。但是为了完成任务,请随意在方法中添加 sn-p 并添加同步到它,如果你愿意(不会伤害)
  • 把它扔到 PrintChar 方法中来处理便便和咯咯笑声?
  • @The_Redhawk 我为您添加了一个同步示例,但它完全没有线程,除非您的老师的意图是向您自己演示线程是如何工作的。
  • @JAsgarov “每次调用 runLater 时都会执行另一个线程”。这不是真的(也许你的意思与你在这里实际陈述的不同)。 Platform.runLater() 不创建或启动任何线程。它接受一个实现Runnable 接口的对象(在此示例中表示为 lambda 表达式)并安排它在已经运行的 FX 应用程序线程上执行。
猜你喜欢
  • 1970-01-01
  • 2021-03-28
  • 2011-03-03
  • 2013-10-27
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多