我对SwingUtilities.invokeLater
的签名感到困惑。它需要Runnable
个对象。这个Runnable对象是否被移交给Event Dispatch Thread?为什么我无法直接在createAndShowGUI
的{{1}}方法上调用run
(如果可能的话)?
关于read articles和EDT如何工作,我有invokeLater,但我对传递的EDT
对象感到困惑。
Runnable
如果我在电话下方再次拨打SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
会怎样?
SwingUtilities.invokeLater
答案 0 :(得分:11)
new Runnable() {
public void run() {
createAndShowGUI();
}
};
这声明了一个匿名类并实例化它的新实例。它基本上等同于:
class Creator implements Runnable {
public void run() {
createAndShowGUI();
}
}
new Creator();
在Java 8 lambdas之前,匿名类是一种进行函数式编程的方法。将Runnable传递给invokeLater
就像传递一个EDT可以随时执行的函数(通过调用它上面的run
)。
最初,它只是创建一个事件来包装它(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传递给invokeLater
或invokeAndWait
可让您在EDT上运行所需的任何代码。
在大多数情况下,立即或几乎立即。美国东部时间非常敏感。 'invokeLater'的'Later'部分只是暗示我们:
run
以异步方式执行。在处理事件之前,调用invokeLater
的线程可能会继续通过调用。当然,如果需要同步调用invokeAndWait
,run
作为替代方案存在。 (虽然应该注意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是什么以及如何访问它的一些误解。