使用两个线程会导致 JavaFX 崩溃 [重复]

Posted

技术标签:

【中文标题】使用两个线程会导致 JavaFX 崩溃 [重复]【英文标题】:Using two threads causes JavaFX to crash [duplicate] 【发布时间】: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/… 为什么你认为你需要并发?这是一项很可能可以使用单线程完成的学校作业,尤其是因为你还没有学过并发 【参考方案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 应用程序线程上执行。

以上是关于使用两个线程会导致 JavaFX 崩溃 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

C++ 无锁队列与多线程崩溃

JavaFX - 建议在 setLayoutX(x) 上使用 relocate(x,y) 移动对象,但我无法修改重定位参数 [重复]

什么原因导致winform程序崩溃

在多线程环境中访问数组会导致应用程序崩溃

什么原因导致winform程序崩溃

iOS MKLocalSearch导致崩溃[重复]