SwingUtilities.invokeLater采用Runnable并在EDT上运行它?

时间:2014-03-10 22:56:10

标签: java multithreading swing awt event-dispatch-thread

我对SwingUtilities.invokeLater的签名感到困惑。它需要Runnable个对象。这个Runnable对象是否被移交给Event Dispatch Thread?为什么我无法直接在createAndShowGUI的{​​{1}}方法上调用run(如果可能的话)?

关于read articlesEDT如何工作,我有invokeLater,但我对传递的EDT对象感到困惑。

Runnable

如果我在电话下方再次拨打SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); 会怎样?

SwingUtilities.invokeLater

2 个答案:

答案 0 :(得分:11)

什么是新的Runnable(){}?

new Runnable() {
    public void run() {
        createAndShowGUI();
    }
};

这声明了一个匿名类并实例化它的新实例。它基本上等同于:

class Creator implements Runnable {
    public void run() {
        createAndShowGUI();
    }
}

new Creator();

在Java 8 lambdas之前,匿名类是一种进行函数式编程的方法。将Runnable传递给invokeLater就像传递一个EDT可以随时执行的函数(通过调用它上面的run)。

invokeLater对Runnable做了什么?

最初,它只是创建一个事件来包装它(InvocationEvent)并将其放在队列的末尾。

排队后,invokeLater通知EDT(如果等待则会唤醒)。 EDT自己的run方法是一个处理事件的无限循环。它看起来像这样(非常复述):

public void run() {
    while(true) {
        EventQueue eq = getEventQueue();

        synchronized(eq) {
            while(eq.hasNextEvent()) {
                processOneEvent(eq.getNextEvent());
            }

            try {
                eq.wait();
            } catch(InterruptedException ie) {}
        }
    }
}

当EDT到达新的InvocationEvent时,它会调用Runnable上的run,它会调用createAndShowGUI

因此,将Runnable传递给invokeLaterinvokeAndWait可让您在EDT上运行所需的任何代码。

多久运行一次?

在大多数情况下,立即或几乎立即。美国东部时间非常敏感。 'invokeLater'的'Later'部分只是暗示我们:

  • run以异步方式执行。在处理事件之前,调用invokeLater的线程可能会继续通过调用。
  • 可以先处理其他事件。

当然,如果需要同步调用invokeAndWaitrun作为替代方案存在。 (虽然应该注意invokeAndWait如果在新的InvocationEvent之前排队,也可能导致处理其他事件。)

异步

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        System.out.println("hello");
    }
});
System.out.println("world");

这可能会打印

hello
world

它可能会打印

world
hello

因为调用线程可能会在事件处理之前(或甚至在处理之前)继续或不继续。这取决于系统的线程调度程序。

同步

SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        System.out.println("hello");
    }
});
System.out.println("world");

这肯定会打印

hello
world

因为调用线程会在继续之前等待run完成(除非抛出异常)。

invokeAndWait使用等待和通知方案来实现此目的。创建一个监视器对象并将其提供给InvocationEvent。发布事件后invokeAndWait调用wait,在{1}}完成后,调用notifyAll调用run。等待和通知方案是在EDT上无法调用invokeAndWait的原因。 EDT无法在一个事件中间停止处理另一个事件。

如果排队多个事件会怎样?

EDT按照排队顺序并根据其优先级逐个处理事件。某些事件也可以合并(常规的InvocationEvent不会)。大多数事件都是正常的优先PaintEvent(类似于调用repaint)通常是低优先级的,某些系统事件具有更高的优先级。

在您给出的具体示例中:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        createAndShowGUI();
    }
});
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        doSomethingElse();
    }
});

由于它们是相同类型的事件且具有相同的优先级,因此doSomethingElse事件将在createAndShowGUI事件完成后处理。

同样,如果这样做了:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                System.out.println("world");
            }
        });
        System.out.println("hello");
    }
});

那将打印

hello
world

因为world Runnable在hello Runnable完成后运行。

答案 1 :(得分:6)

您可以使用任何线程中的SwingUtilities.invokeLater()对要在EDT上运行的任务进行排队。这在一些情况下很有用,尤其是在多线程应用程序中,作为从非GUI线程更新GUI元素的便捷方式,以及从main()启动应用程序(您的示例提示)时在主线程中运行,而不是在EDT中运行。

E.g:

// main is not executed in the EDT
public static void main (String[] args) {

    // create and initialize GUI in the EDT to avoid potential thread-safety issues.
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });

}

您可能希望查看The Event Dispatch Thread上的官方教程,该教程应该澄清您对EDT是什么以及如何访问它的一些误解。