【问题标题】:Java notify when AWT dispatching thread has executed runnable当 AWT 调度线程执行 runnable 时 Java 通知
【发布时间】:2014-05-11 14:55:00
【问题描述】:

所以基本上我会为我的应用程序更新 GUI,所以我使用静态方法

void javax.swing.SwingUtilities.invokeLater(Runnable doRun)

但我希望将我的初始化代码放在可运行文件中,而将逻辑代码放在外面。但是,如果我调用 invokeLater 方法,我的主线程和 AWT 线程将处于竞争状态,因为我的主线程将在调用 invokeLater 方法之后尝试调用我的逻辑代码,但由于JFrame(就我而言)尚未初始化,因此我的主线程很可能会赢得比赛并因此引发异常。

我认为我可以等待可运行对象被事件调度线程执行,然后在可运行对象初始化初始化代码时通知它。但是由于某种原因,下面的代码(缩写)不起作用。

对于轻量级应用程序,runnable 可能会赢得竞争条件,因此不会产生任何异常,但在我的应用程序中,初始化代码更长。

SSCCE:

public class SSCCE {
    private static JFrame frame;
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            public void run() {
                synchronized (this) {
                    frame = new JFrame();
                    frame.setTitle("JFrame");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLocationRelativeTo(null);
                    this.notifyAll();
                    System.out.println("Notified all");
                }
            }
        };
        synchronized (r) {
            try {
                System.out.println("Waiting"); // EDIT this line was after the wait method, and therefore it didn't execute.
                r.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException("Unexpected disruption whilst waiting for the \"Event-Dispatching thread\".",e);
            }
        }
        SwingUtilities.invokeLater(r);
        frame.setVisible(true);
        frame.getContentPane().add(new JLabel("Hello World!"));
        frame.pack();
    }
}

提前致谢!

【问题讨论】:

  • 抱歉,这背后的动机是什么?
  • 所有 Swing GUI 代码都应该在事件调度线程上执行。使用 invokeLater() 的目的是向 EDT 添加代码。无需同步您的 Runabble。我真的认为你把事情复杂化了。只需将所有代码放入 EDT。如果您有一个长时间运行的任务,那么您可以在 GUI 已经创建并使其可见后从您的 init 代码中调用该任务。
  • @HovercraftFullOfEels 我正在创建一个将更新 GUI 的应用程序,并且 invokeLater 应该在 AWT 事件调度线程上异步运行主线程,但是因为我试图直接访问 JFrame之后,它会抛出一个异常,因为它还没有被初始化。
  • Linus,你告诉我们的 Y 部分听起来像是 XY Problem——你计划的代码解决方案——一个可能完全错误的计划。我想知道 X 部分,需要这样一个方案的基本原理。
  • @camickr 那么,创建一个像CreateAndShowGUI 这样的方法是我应该做的吗?然后在初始化代码之后从我的可运行文件中调用它?

标签: java multithreading swing race-condition


【解决方案1】:

你的错误很简单:你正在调用(好吧,如果它有效的话,你就是)invokeLater 你试图 wait 得到结果:

synchronized (r) {
  try {
    …
    r.wait(); // <- here you are waiting
  } catch (InterruptedException e) {
    …
  }
}
SwingUtilities.invokeLater(r); // <- never reached, you are waiting above

因此,显而易见的解决方法是先调用SwingUtilities.invokeLater(r);,然后再调用wait


但是那已经相当过时了,有一个方法 SwingUtilities.invokeAndWait(r); 可以在 EDT 中调度 Runnable 并等待其完成。所以你不需要自己编程同步和通知。


但是请注意,您在 invokeLater 之后的 main 方法中的行正在操纵 UI,因此应该像 JFrame 的构造一样放在 EDT 中,否则您仍然违反 Swing 的线程策略并且仍然可能会出现竞争条件。

您的main 线程可能会同时向 EDT 执行操作,并且如果您愿意,它可能会等待 UI 构建。但它应该操纵 UI 本身。这种操作也会与您的陈述相矛盾,即您希望拥有“外部逻辑代码”,因为操作 JFrame 不属于“逻辑代码”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多