【问题标题】:Returning values from Swing using invokeAndWait使用 invokeAndWait 从 Swing 返回值
【发布时间】:2010-03-08 13:34:57
【问题描述】:

我一直在使用以下方法来创建组件并从 Swing 向/从 EDT 外部返回值。例如,以下方法可以是JFrame 的扩展,创建一个JPanel 并将其添加到父JFrame

public JPanel threadSafeAddPanel() {

    final JPanel[] jPanel = new JPanel[1];

    try {
        EventQueue.invokeAndWait(new Runnable() {
            public void run() {
                jPanel[0] = new JPanel();
                add(jPanel[0]);
            }
        });
    } catch (InterruptedException ex) {
    } catch (InvocationTargetException ex) {
    }

    return jPanel[0];
}

本地 1 长度数组用于从 EDT 中调用的 Runnable 内部传输“结果”。好吧,它看起来“有点” hacky,所以我的问题是:

  1. 这有意义吗?有其他人在做这样的事情吗?
  2. 长度为 1 的数组是传输结果的好方法吗?
  3. 有更简单的方法吗?

【问题讨论】:

    标签: java swing event-dispatch-thread invokeandwait


    【解决方案1】:

    虽然这种方法在某些情况下可能有意义,但大多数时候它是无用的。

    原因是大多数(如果不是全部)组件的创建将始终从 EDT 进行,这是始终从 EDT 执行的用户操作(单击菜单项或按钮)的结果。

    如果您在创建面板之前有大量工作要执行并且您不想阻止 EDT,那么您应该按照其他人的建议使用 SwingWorker 或为长任务提供支持的 Swing 框架(无论如何,通常内部都基于 SwingWorker,但不一定)。

    关于你的问题 2,很遗憾,你没有很多方法可以做到这一点:

    • 像以前一样使用 1 项数组,即 最简单但也是最丑陋的解决方案
    • 创建一个 ItemHolder 类(请参阅 下面)几乎相同, 需要更多的工作并且是 更清洁,在我看来
    • 最后,使用 java.util.concurrent 设施(Future 和 Callable); 那将是我认为最干净的, 但也需要付出最大的努力

    这里是简化的 ItemHolder 类:

    public class ItemHolder<T> {
        public void set(T item) {...}
        public T get() {...}
        private T item;
    }
    

    【讨论】:

    • +1 到这里关于忽略线程问题的建议,因为这个方法很可能无论如何都会从 EDT 调用。我只会断言 isEventDispatchThread() 并删除所有样板文件 - 延迟问题,直到有人真正从另一个线程调用它,因为 YAGNI。
    【解决方案2】:
    • 吞下异常而不记录它们:糟糕!! - 当你在 2 小时的 bug 搜寻后遇到类似的事情时,你会恨自己
    • 不,数组不是一个好方法;一方面,它没有为调用代码提供简单的方法来等待 EDT 线程在获取结果之前执行Runnable
    • 有一个专门为这种事情设计的类:SwingWorker

    【讨论】:

    • 1.你是对的 :-) 2. 好的,但是 invokeAndWait 会这样做。 3. 也许我应该使用它,虽然对于简单的“getter”来说感觉有点矫枉过正。
    • 1. SwingWorker 不是您可以用来修复 Swing 应用程序中所有线程问题的锤子。 2. SwingWorker 用于在后台线程上运行,但这个问题是关于创建一个面板,无论如何它都必须在 EDT 上运行——这与 SwingWorker 帮助你做的完全相反。
    【解决方案3】:
    1. a) 这是有道理的。 b) 我不知道。
    2. 无懈可击。
    3. invokeAndWait 调用之外创建 JPanel

    //为了安抚markdown添加了这一行

    public JPanel threadSafeAddPanel() {
        final JPanel jPanel = new JPanel();
        try {
            EventQueue.invokeAndWait(new Runnable() {
                public void run() {
                    add(jPanel);
                }
            });
        } catch (InterruptedException ex) {
        } catch (InvocationTargetException ex) {
        }
        return jPanel;
    }
    

    【讨论】:

    • 虽然 3. 很可能有效,但严格来说是不允许的。
    • 我同意 Joonas 的观点,在 EDT 之外创建 Swing 组件会开创一个不好的先例,应该避免。
    【解决方案4】:

    您可以轻松检查当前线程是否为 EDT,然后在该上下文中正确且更简单地执行。至于使用最终的数组来获取返回值,当你必须使用这样的匿名内部类时,这是最简单的方法。

    public JPanel threadSafeAddPanel() throws InterruptedException, 
            InvocationTargetException {
        if (EventQueue.isDispatchThread()) {
            JPanel panel = new JPanel();
            add(panel);
    
            return panel; 
        } else {
            final JPanel[] jPanel = new JPanel[1];
            EventQueue.invokeAndWait(new Runnable() {
                public void run() {
                    jPanel[0] = new JPanel();
                    add(jPanel[0]);
                }
            });
    
            return jPanel[0];
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-07
      • 2020-05-22
      • 1970-01-01
      相关资源
      最近更新 更多