【问题标题】:Function continues after notifyAll(), but Thread terminates after function returnsnotifyAll() 后函数继续,但函数返回后线程终止
【发布时间】:2020-10-10 09:43:00
【问题描述】:

基本上,线程 A wait() 在锁定对象上,然后是线程 B notifyAll()。线程 A 上的函数完成,但不返回正常操作。

值得注意的是,我使用的是 JavaFX。为了将我的 FX 线程与我的工作线程分开,我创建了一个新线程以防止在我锁定它时出现问题等。

这是我的代码(简化为有问题的函数):

public class ShortcutManager {

    private static final StringProperty STATE = new SimpleStringProperty("Create Shortcut");

    public static void create() {
        Thread creationThread = new Thread(() -> {
            // Do some work (sends logs to the logger)
            String result = prompt();
            if (result != null) {
                // Do more work (sends logs to the logger)
            } else {
                // Do other work (sends logs to the logger)
            }
        }
        creationThread.start();
    }

private static String prompt() {
    STATE.setValue("Waiting for user...");
    String result = popup.showAndWait();
    System.out.println("Result: \"" + result + "\"");
    return result;
}
public class CmlPopup implements Initializable {

    private boolean isClosed = false;

    public synchronized String showAndWait() {
        isClosed = false;
        Platform.runLater(() -> {
            stage.show();
        });
        while (!isClosed) {
            try {
                System.out.println("Waiting.");
                wait();
                System.out.println("Notified.");
            } catch (InterruptedException ex) {

            }
        }
        return getResult();
    }

    public synchronized void close() {
        stage.close();
        isClosed = true;
        System.out.println("Closing and notifying");
        notifyAll();
    }

}

现在 - 当舞台有 showAndWait() 函数时,为什么我要自己锁定它?因为这也不起作用,而且它最终会暂停 FX 线程而不是创建线程,因为 show() 和 showAndWait() 都必须在 FX 线程上发生。

我的输出如下:

. . .

ShortcutManager State: Waiting for user...
Waiting.
Closing and notifying.
Notified.

然后仅此而已。不打印结果;线程没有其他工作。我的 FX 线程和其他工作线程运行良好,但这个似乎只是终止,即使还有更多工作要做。

附:在你批评我之前,我在打算保留它们的地方使用 Loggers,而当我打算删除它们时使用 System.out。

【问题讨论】:

  • 我不是 JavaFX 专家,但您的方法听起来很危险。您通常会这样做的方式是创建一些其他线程来完成工作,当线程完成时,您会告诉 UI 线程 repaint() (在 Swing repaint() 中是线程安全的)。 paint() 方法会注意到您的工作已准备就绪,并执行适当的 UI 操作以使事情变得漂亮。
  • 还有其他方法。就像使用某种 util.concurrent Queue 一样。您有一个线程添加到该队列,并且 UI 线程检查该队列。我认为这种方法通常是可取的。使用 wait() 和 notify() 和 synchronized 使用了太多的低级原语,很可能会出现一些细微的错误。首选来自 util.concurrent 的构造。
  • 无法重现该问题(当然我需要更改一些内容,例如修复语法错误以编译代码)。请注意,您可以使用 Platfrom.runLater 从 javafx 应用程序线程中轻松检索信息以检索数据,然后使用该信息填充您在后台线程上创建的 CompletableFuture。例如,请参阅我的帖子:stackoverflow.com/a/58761795/2991525
  • 使用更高级别的 API 进行线程处理总是比尝试使用线程原语重现相同的功能要好。 @fabian 提供的示例就是一个很好的例子;另见stackoverflow.com/questions/14941084/…

标签: java multithreading javafx


【解决方案1】:

首先 - 感谢那些提供评论的乐于助人的人。尽管您的解决方案对我不起作用,但它们为我指明了正确的方向。

现在,我的解决方案是:我只是将弹出结果、阻塞等包装在 CompletableFuture 中。

public class CmlPopup<R, D> implements Initializable {
    
    private final Object closeLock = new Object();
    private volatile boolean isClosed = false;

    ----

    public CompletableFuture<R> showAndGet() {
        Platform.runLater(() -> {
            stage.show();
        });
        return CompletableFuture.supplyAsync(() -> {
            synchronized (closeLock) {
                while (!isClosed) {
                    try {
                        closeLock.wait();
                    } catch (InterruptedException ex) {
                        
                    }
                }
                return contentManager.getResult();
            }
        }

    public void showThenRun(BiConsumer<? super R, ? super Throwable> action) {
        CompletableFuture.supplyAsync(() -> {
            synchronized (closeLock) {
                while (!isClosed) {
                    try {
                        closeLock.wait();
                    } catch (InterruptedException ex) {
                        
                    }
                }
                return contentManager.getResult();
            }
        }).whenComplete(action);
        Platform.runLater(() -> {
            stage.show();
        });
    }

    public void close() {
        stage.close();
        synchronized (closeLock) {
            isClosed = true;
            closeLock.notifyAll();
        }
    }

}
public class ShortcutManager {

    private static final CmlPopup<String, Integer> POPUP = new CmlPopup(...);

    ----

    private static void prompt(File userData) {
        POPUP.showThenRun((String result, Throwable ex) -> {
            System.out.println("Result: \"" + result+ "\"");
            ShortcutManager.identifyAndGridImages(result, userData);
        });
    }
    
    ----
    
}

这完全可靠。无论出于何种原因,对象本身的 CompletableFuture 永远不会运行 whenComplete 函数,即使未来被认为是“完整的”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多