如何锁定Java线程,以便在Platform.runLater完成之前锁定其他线程
在我的OSGi中,可以随时启动和停止基于OSGi的应用程序包。面向UI的捆绑包向“桌面”捆绑包注册javafx节点。必须使用如何锁定Java线程,以便在Platform.runLater完成之前锁定其他线程,java,multithreading,javafx-8,Java,Multithreading,Javafx 8,在我的OSGi中,可以随时启动和停止基于OSGi的应用程序包。面向UI的捆绑包向“桌面”捆绑包注册javafx节点。必须使用Platform.runLater(),在JavaFXUI线程上操作UI,这将安排它在以后执行 我想确保一次只有一个呼叫者可以修改桌面。我使用了一个锁和一个条件来实现这一点 public class DesktopContent implements ContentManager { private final Pane desktop; private final
Platform.runLater()
,在JavaFXUI线程上操作UI,这将安排它在以后执行
我想确保一次只有一个呼叫者可以修改桌面。我使用了一个锁和一个条件来实现这一点
public class DesktopContent implements ContentManager {
private final Pane desktop;
private final AtomicReference<Node> weather = new AtomicReference<>();
private final ReentrantLock lock = new ReentrantLock();
@Override
public void setWeatherWidget(Node node) {
updateFX(() -> replaceNode(desktop.getChildren(), weather, node));
}
private void updateFX(Runnable runnable) {
lock.lock();
Condition condition = lock.newCondition();
try {
if (Platform.isFxApplicationThread()) {
runnable.run();
} else {
updateFX(runnable, condition);
condition.await();
}
} catch (InterruptedException e) {
logger.log(Level.WARNING, "While waiting for 'updating' condition", e);
} finally {
lock.unlock();
}
}
private void updateFX(Runnable runnable, Condition condition) {
Platform.runLater(() -> {
lock.lock();
try {
runnable.run();
} finally {
condition.signal();
lock.unlock();
}
});
}
private void replaceNode(List<Node> children, AtomicReference<Node> current, Node newNode) {
SequentialTransition st = new SequentialTransition();
ObservableList<Animation> transformations = st.getChildren();
Node oldNode = current.getAndSet(newNode);
if (oldNode != null) {
FadeTransition ft = new FadeTransition(Duration.millis(350), oldNode);
ft.setToValue(0d);
ft.setOnFinished(e -> children.remove(oldNode));
transformations.add(ft);
}
if (newNode != null) {
newNode.setLayoutX(100d);
newNode.setLayoutY(100d);
FadeTransition ft = new FadeTransition(Duration.millis(350), newNode);
ft.setFromValue(0d);
ft.setToValue(1d);
children.add(newNode);
transformations.add(ft);
}
st.play();
}
公共类DesktopContent实现ContentManager{
私人最终版桌面;
私有最终原子参考天气=新原子参考();
private final ReentrantLock lock=new ReentrantLock();
@凌驾
公共void setWeatherWidget(节点){
updateFX(()->replaceNode(desktop.getChildren(),weather,node));
}
私有void updateFX(可运行可运行){
lock.lock();
Condition Condition=lock.newCondition();
试一试{
if(Platform.isFxApplicationThread()){
runnable.run();
}否则{
updateFX(可运行,条件);
条件wait();
}
}捕捉(中断异常e){
logger.log(Level.WARNING,“在等待‘更新’条件时”,e);
}最后{
lock.unlock();
}
}
私有void updateFX(可运行,可运行,条件){
Platform.runLater(()->{
lock.lock();
试一试{
runnable.run();
}最后{
条件。信号();
lock.unlock();
}
});
}
私有void replaceNode(列出子节点、原子引用当前节点、节点newNode){
顺序转换st=新的顺序转换();
ObservableList transformations=st.getChildren();
Node oldNode=current.getAndSet(newNode);
if(oldNode!=null){
FadeTransition ft=新的FadeTransition(持续时间.毫秒(350),旧节点);
ft.setToValue(0d);
ft.setOnFinished(e->children.remove(oldNode));
添加(ft);
}
if(newNode!=null){
newNode.setLayoutX(100d);
newNode.setLayoutY(100d);
FadeTransition ft=新的FadeTransition(持续时间.毫秒(350),新节点);
ft.setFromValue(0d);
ft.setToValue(1d);
添加(newNode);
添加(ft);
}
圣普拉();
}
}
我不确定这是否正确。如果调用者不在应用程序线程上,框架将安排runnable的执行,调用者将等待直到满足条件
但是,如果同时有人调用setWeatherWidget,我想它会很高兴地被授予锁,安排另一个稍后运行。这意味着我完全可以跳过任何锁定
如何使用锁正确处理此问题?引入另一个“全局”锁?或者这是过度的工程设计,所有这些锁定业务都是不必要的?我认为您可以做到这一点,而不必涉及锁、条件等较低级别的细节。本质上,您希望对setWeatherWidget
单线程(一次只能调用一个)进行所有调用,当然,要确保必须在FX应用程序线程上执行的代码已经完成。关键是FX应用程序线程已经是单个线程:因此,您可以通过向FX应用程序线程发送请求并阻塞直到这些请求完成来实现这一点:
public class DesktopContent implements ContentManager {
private final Pane desktop;
private final AtomicReference<Node> weather = new AtomicReference<>();
@Override
public void setWeatherWidget(Node node) {
updateFX(() -> replaceNode(desktop.getChildren(), weather, node));
}
private void updateFX(Runnable runnable) {
try {
if (Platform.isFxApplicationThread()) {
runnable.run();
} else {
FutureTask<Void> task = new FutureTask<>(runnable, null);
Platform.runLater(task);
// block until task completes:
task.get();
}
} catch (InterruptedException e) {
logger.log(Level.WARNING, "While waiting for 'updating' condition", e);
}
}
private void replaceNode(List<Node> children, AtomicReference<Node> current, Node newNode) {
SequentialTransition st = new SequentialTransition();
ObservableList<Animation> transformations = st.getChildren();
Node oldNode = current.getAndSet(newNode);
if (oldNode != null) {
FadeTransition ft = new FadeTransition(Duration.millis(350), oldNode);
ft.setToValue(0d);
ft.setOnFinished(e -> children.remove(oldNode));
transformations.add(ft);
}
if (newNode != null) {
newNode.setLayoutX(100d);
newNode.setLayoutY(100d);
FadeTransition ft = new FadeTransition(Duration.millis(350), newNode);
ft.setFromValue(0d);
ft.setToValue(1d);
children.add(newNode);
transformations.add(ft);
}
st.play();
}
}
公共类DesktopContent实现ContentManager{
私人最终版桌面;
私有最终原子参考天气=新原子参考();
@凌驾
公共void setWeatherWidget(节点){
updateFX(()->replaceNode(desktop.getChildren(),weather,node));
}
私有void updateFX(可运行可运行){
试一试{
if(Platform.isFxApplicationThread()){
runnable.run();
}否则{
FutureTask任务=新的FutureTask(可运行,空);
Platform.runLater(任务);
//阻止,直到任务完成:
task.get();
}
}捕捉(中断异常e){
logger.log(Level.WARNING,“在等待‘更新’条件时”,e);
}
}
私有void replaceNode(列出子节点、原子引用当前节点、节点newNode){
顺序转换st=新的顺序转换();
ObservableList transformations=st.getChildren();
Node oldNode=current.getAndSet(newNode);
if(oldNode!=null){
FadeTransition ft=新的FadeTransition(持续时间.毫秒(350),旧节点);
ft.setToValue(0d);
ft.setOnFinished(e->children.remove(oldNode));
添加(ft);
}
if(newNode!=null){
newNode.setLayoutX(100d);
newNode.setLayoutY(100d);
FadeTransition ft=新的FadeTransition(持续时间.毫秒(350),新节点);
ft.setFromValue(0d);
ft.setToValue(1d);
添加(newNode);
添加(ft);
}
圣普拉();
}
}
我通常使用CountDownLatch
而不是条件,因为它避免了一次竞争:signal()
可以在wait()
之前调用。除此之外,我并不完全明白你想要实现什么。Runnables在一个线程中执行,因此不能并发执行,从而保证“一次只有一个调用者可以修改桌面”,而不需要任何锁定。@SergeyTachenov在wait()
之前不可能调用signal()
。在执行runLater时,第一个updateFX方法已经持有锁,因此除非第一个方法执行wait()
,否则runLater无法获得锁。(但是使用CountDownLatch会使代码更短。)@VGR,你说得对。我被纠正了。我见过