我正在嘗試調用外部命令並在製作時在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