我正在尝试调用外部命令并在制作时在TextArea
中输出其输出。我阅读了关于JavaFX并发性的文档,我相信我做了我必须做的工作。正确管理JavaFX中的线程
我使用Task
类运行我的工作:
public synchronized void run(ProcessBuilder processBuilder) throws IOException, InterruptedException {
ExternalCommandRunner self = this;
Thread taskThread = new Thread(new Task<Void>() {
@Override
public Void call() throws Exception {
setActive();
try {
runningProcess = processBuilder.start();
StreamPrinter inputStream = new StreamPrinter(runningProcess.getInputStream(), self::handleLog);
StreamPrinter errorStream = new StreamPrinter(runningProcess.getErrorStream(), self::handleLog);
outputTextArea.clear();
new Thread(inputStream).start();
new Thread(errorStream).start();
runningProcess.waitFor();
return null;
} finally {
stop.fire();
setInactive();
}
}
});
taskThread.setDaemon(true);
taskThread.start();
taskThread.join();
}
private void handleLog (String line) { Platform.runLater(() -> outputTextArea.appendText(line + "\n")); }
private void setActive () { setState(STOP_ACTIVE_ICON , false) ; }
private void setInactive() { setState(STOP_INACTIVE_ICON, true) ; }
private void setState (String iconPath, boolean disableButton) {
stop.setGraphic(imageViewFromResource(iconPath, Resources.class));
stop.setDisable(disableButton);
}
不过,我发现了以下异常:
Exception in thread "Thread-5" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(Unknown Source)
at javafx.scene.Parent$2.onProposedChange(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(Unknown Source)
at com.sun.javafx.scene.control.skin.ButtonSkin.handleControlPropertyChanged(Unknown Source)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(Unknown Source)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(Unknown Source)
at javafx.beans.value.WeakChangeListener.changed(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source)
at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source)
at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.ObjectPropertyBase.set(Unknown Source)
at javafx.css.StyleableObjectProperty.set(Unknown Source)
at javafx.beans.property.ObjectProperty.setValue(Unknown Source)
at javafx.scene.control.Labeled.setGraphic(Unknown Source)
at com.dici.javafx.components.ExternalCommandRunner.setState(ExternalCommandRunner.java:66)
at com.dici.javafx.components.ExternalCommandRunner.setActive(ExternalCommandRunner.java:63)
at com.dici.javafx.components.ExternalCommandRunner.access$000(ExternalCommandRunner.java:19)
at com.dici.javafx.components.ExternalCommandRunner$1.call(ExternalCommandRunner.java:39)
at com.dici.javafx.components.ExternalCommandRunner$1.call(ExternalCommandRunner.java:36)
at javafx.concurrent.Task$TaskCallable.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
stop
简直是Button
。我不正确的是什么?从文档,使用Task
这种方式应该是足够的...
那是因为我阻止用'taskThread.join()'的FX应用程序线程?我需要这个来防止其他线程在命令运行时干扰'TextArea'。我要尝试另一种同步方法 – Dici
错误提示您正在从后台线程更新UI控件(特别是在标签上设置图形)。 FX应用程序线程的所有更新都必须发生。您也不能阻止FX应用程序线程,通过调用'join()',您似乎正在执行该线程。所以我认为这里有很多错误。也许看到http://stackoverflow.com/questions/30249493/using-threads-to-make-database-requests这可能会有所帮助 - 虽然你的用例看起来有点不同。如果您描述了您正在尝试实现的内容并创建了[MCVE],则可能会有所帮助。 –
我现在使用'Semaphore'来阻止对该方法的访问而不阻塞FX主线程,但仍然出现错误。根据Task的文档,使用'new Thread(新任务<...>(){...}).start()'应该可以工作,所以我很困惑。请注意,我现在可以正确更新'TextArea',但不是按钮中的图标 – Dici