1 回答

TA貢獻1934條經驗 獲得超2個贊
使用 aSemaphore很可能不是正確的方法。您想要的是進入嵌套事件循環,而不是使用阻塞機制。從閱讀 API 來看,你似乎把事情過于復雜化了。同樣,您所需要的只是在一個 UI 線程上進入嵌套事件循環,然后在另一個 UI 線程完成其工作后退出該循環。我相信以下內容可以滿足您的要求:
import java.awt.EventQueue;
import java.awt.SecondaryLoop;
import java.awt.Toolkit;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javafx.application.Platform;
import javax.swing.SwingUtilities;
public class Foo {
public static <T> T getOnFxAndWaitOnEdt(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier, "supplier");
if (!EventQueue.isDispatchThread()) {
throw new IllegalStateException("current thread != EDT");
}
final SecondaryLoop loop = Toolkit.getDefaultToolkit()
.getSystemEventQueue()
.createSecondaryLoop();
final AtomicReference<T> valueRef = new AtomicReference<>();
Platform.runLater(() -> {
valueRef.set(supplier.get());
SwingUtilities.invokeLater(loop::exit);
});
loop.enter();
return valueRef.get();
}
public static <T> T getOnEdtAndWaitOnFx(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier, "supplier");
if (!Platform.isFxApplicationThread()) {
throw new IllegalStateException(
"current thread != JavaFX Application Thread");
}
final Object key = new Object();
final AtomicReference<T> valueRef = new AtomicReference<>();
SwingUtilities.invokeLater(() -> {
valueRef.set(supplier.get());
Platform.runLater(() -> Platform.exitNestedEventLoop(key, null));
});
Platform.enterNestedEventLoop(key);
return valueRef.get();
}
}
JavaFX 9 中添加了和方法,盡管 JavaFX 8 中也有等效的內部方法。使用的原因是因為在 lambda 表達式內部使用時,局部變量必須是 Final 或有效的 Final Platform#enterNestedEventLoop。然而,由于通知單獨線程的方式,我不認為 和方法提供的易變性語義是嚴格需要的,但我使用了這些方法以防萬一。Platform#exitNestedEventLoopAtomicReference#get()#set(T)AtomicReference
以下是使用上述內容從事件調度線程顯示模式 JavaFX 對話框的示例:
Optional<T> optional = Foo.getOnFxAndWaitOnEdt(() -> {
Dialog<T> dialog = new Dialog<>();
// configure dialog...
return dialog.showAndWait();
});
上述實用程序方法用于從事件調度線程到JavaFX 應用程序線程進行通信,反之亦然。這就是為什么需要輸入嵌套事件循環,否則 UI 線程之一將必須阻塞,從而凍結關聯的 UI。如果您位于非 UI 線程上并且需要在等待結果時在 UI 線程上運行操作,則解決方案要簡單得多:
// Run on EDT
T result = CompletableFuture.supplyAysnc(/*Supplier*/, SwingUtilities::invokeLater).join();
// Run on FX thread
T result = CompletableFuture.supplyAsync(/*Supplier*/, Platform::runLater).join();
調用join()將阻塞調用線程,因此請確保不要從任一 UI 線程調用該方法。
添加回答
舉報