使用两个线程会导致 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(() -> 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 崩溃 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
JavaFX - 建议在 setLayoutX(x) 上使用 relocate(x,y) 移动对象,但我无法修改重定位参数 [重复]