JavaFX操作中的切换按钮将由2个单独的线程访问。
1 一旦用户点击(切换按钮状态),就会调用一个线程 a)在操作系统中做一些事情 b)检查(a)是否成功 c)在成功/退出时退出,并在失败时将切换按钮返回到先前的状态
2 另一个线程将监视与先前操作异步的事件,并且在特定事件的情况下,它将更改按钮状态。
在锁定按钮状态方面,我是否需要在线程 1 和 2 之间提供同步?
编辑:James_D提出的想法似乎是合理的,但我只是想提出一个替代方案(其有效性仍有待证明)。 如何使用同步代码块,并使用锁定对特定按钮的引用,例如:// getting the reference to the button
@FXML
private ToggleButton tButtonToBeSynchronized
// Thread1
synchronized(tButtonToBeSynchronized) {
// do stuff with button upon user click
}
// Thread2
synchronized(tButtonToBeSynchronized) {
// poll system every X seconds
// when asynchronous event occurs (not related to UI events)
// update tButtonToBeSynchronized state
}
如果不同的Controller类调用它们会有用吗? (假设引用tButtonToBeSynchronized
是通过引用传递的 - 而不是FXML框架的值?
答案 0 :(得分:2)
与大多数UI工具包一样,JavaFX采用单线程模型。您应该只从FX Application Thread访问属于场景图的一部分节点的状态。因此,切换按钮不是原子操作,并且您描述的代码不能保证按照您当前设置的方式工作。在Java 8中,它可能会抛出RuntimeException
。
JavaFX提供了与后台线程实现互操作的功能。其中最低级别为Platform.runLater(Runnable r)
,在FX应用程序线程上执行r
。因此,您的监视器线程(问题中的第2项)应该使用
Platform.runLater( () -> toggleButton.setSelected(...) );
还有javafx.concurrent
API。这提供了一个Task
类,它同时充当Runnable
和java.util.concurrent.FutureTask
,另外还有一组回调方法,用于提交要在FX应用程序线程上执行的代码在Task
生命周期的各个阶段。
所以你应该在你的问题中实现第1项:
ExecutorService exec = ... ; // e.g. Executors.newCachedThreadPool();
toggleButton.selectedItemProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
Task<Void> task = new Task<Void>() {
@Override
public Void call() throws Exception {
// do something on OS
// throw exception if failed
return null ;
}
};
task.setOnFailed(event -> toggleButton.setSelected(wasSelected));
exec.submit(task);
}
});
如果您希望返回表示成功或失败的值,则可以执行
Task<Boolean> task = new Task<Boolean>() {
@Override
public Boolean call() {
// do work...
boolean successful = ... ;
return successful ;
}
};
task.setOnSucceeded( event -> {
boolean wasSuccessful = task.getValue();
// ...
});