Kav*_*ora 2 java multithreading javafx runnable
我有一个简单的应用程序,它在后台更新数据,当它更新时,它会禁用所有其他按钮并启用 TextArea 来显示进度。
脚步:
禁用 mainUI 中的所有其他按钮(按钮名称:plotButton)
启用显示更新已开始的 TextArea(TextArea 名称:infoLogTextArea)
然后只启动更新方法(update() 抛出异常)。
这是代码:
@FXML
public void handleUpdateButton() {
infoLogTextArea.setVisible(true);
infoLogTextArea.appendText("Please wait while downloading data from internet.....\n");
plotButton.setDisable(true);
updateButton.setDisable(true);
if(c!=null) {
Runnable task = new Runnable() {
@Override
public void run() {
// Thread.sleep(10000); -> sleep for 10secs
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
c.updateData();
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};
new Thread(task).start();
}else {
System.out.println("c not initialized");
}
}
Run Code Online (Sandbox Code Playgroud)
现在代码运行良好,但有时第 1 步和第 2 步没有执行,它开始第 3 步(更新),这会冻结程序。如果我将 Thread.sleep(10 secs) 放在第 2 步和第 3 步之间,则它完全正常。(它在代码中注释)
但是有人能解释一下幕后发生的事情Platform.runLater()吗,为什么总是不工作?
Platform 类的文档很好地解释了一切:
public static void runLater(Runnable runnable)
Run Code Online (Sandbox Code Playgroud)
在将来某个未指定的时间在 JavaFX 应用程序线程上运行指定的 Runnable 。该方法可以从任何线程调用,它将把 Runnable 发布到事件队列,然后立即返回给调用者。Runnables 按照发布的顺序执行。传入 runLater 方法的 runnable 将在任何 Runnable 传入后续调用 runLater 之前执行。如果在 JavaFX 运行时关闭后调用此方法,则该调用将被忽略:Runnable 将不会被执行,也不会抛出任何异常。注意:应用程序应避免使用过多待处理的 Runnable 来淹没 JavaFX。否则,应用程序可能会变得无响应。鼓励应用程序将多个操作批量化为更少的 runLater 调用。此外,长时间运行的操作应尽可能在后台线程上完成 ,从而释放 JavaFX 应用程序线程以进行 GUI 操作。
在 FX 运行时初始化之前不得调用此方法。对于扩展 Application 并使用 Java 启动器或 Application 类中的启动方法之一来启动应用程序的标准 JavaFX 应用程序,FX 运行时由启动器在加载 Application 类之前初始化。
因此,使用 runLater 仅更新非 JavaFX 线程上的任何 UI 元素,并将任何繁重的工作留在后台线程上。
JavaFX 应用程序在应用程序线程上运行,该线程处理所有 UI 元素。这意味着如果您单击按钮 A 并单击该按钮启动需要 5 秒才能完成的方法 A,然后单击该按钮一秒钟后,您尝试单击启动方法 B 的按钮 B,方法 B 将不会启动,直到方法一个结束。或者可能按钮 B 在方法 A 完成之前甚至无法工作,我对那里的细节有点模糊。
阻止应用程序冻结的一个好方法是使用线程。要解决上述情况,单击按钮 A 将启动启动新线程的方法 A。然后线程可以在不锁定 UI 并阻止您单击按钮 B 的情况下完成它想要完成的时间。
现在,说一些需要在应用程序线程上的方法 A 中的内容,例如,它更新了一个 UI 组件,如标签或文本字段。然后在方法 A 中的线程中,您需要将影响 UI 的部分放入 a 中Platform.runLater(),以便它与 UI 的其余部分一起在应用程序线程上运行。
对于您的示例,这意味着您有两个选择。
1. 根本不要使用线程,因为您不希望用户在更新发生时与 UI 交互。
2.搬出c.updateData()去是Platform.runLater()这样的:
Runnable task = new Runnable() {
@Override
public void run() {
c.updateData();
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};
Run Code Online (Sandbox Code Playgroud)
其中任何一个都可以工作,但您现在所做的是在应用程序线程上,然后启动另一个线程,其唯一目的是在应用程序线程上运行某些内容。
| 归档时间: |
|
| 查看次数: |
9571 次 |
| 最近记录: |