EDT 究竟是什么时候开始的?哪一行代码负责它?
我的猜测是“someSwingComponent.setVisible(true)”可以解决问题,但我不确定。
谢谢!
EDT 究竟是什么时候开始的?哪一行代码负责它?
我的猜测是“someSwingComponent.setVisible(true)”可以解决问题,但我不确定。
谢谢!
The inner workings of Swing are JVM-specific. Different JVMs start the Event Dispatch Thread (EDT) based on differing criteria. In general though:
The EDT starts when it receives its first
AWTEvent
.
The stack traces below reaffirm this point. Take for example the following main
method.
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setVisible(true);
}
In the example above, the line of code responsible for starting the EDT is frame.setVisible(true);
The above main
method was executed on two different JVMs. A breakpoint was placed at EventQueue.initDispatchThread
. When the breakpoint was hit, the following stack traces were noted.
Using the Mac's JDK on the AWT-AppKit
thread:
EventQueue.initDispatchThread() line: 906
EventQueue.wakeup(boolean) line: 1109
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
SunToolkit.wakeupEventQueue(EventQueue, boolean) line: 348
PostEventQueue.postEvent(AWTEvent) line: 2137
SunToolkit.postEvent(AppContext, AWTEvent) line: 583
SunToolkit.executeOnEventHandlerThread(PeerEvent) line: 654
SunToolkit.executeOnEventHandlerThread(Object, Runnable) line: 631
EventFactoryProxy.windowMoved(CWindow) line: 89
Using Oracle's JDK for Windows on the main
thread:
java.awt.EventQueue.initDispatchThread() line: 861
java.awt.EventQueue.postEventPrivate(java.awt.AWTEvent) line: 199
java.awt.EventQueue.postEvent(java.awt.AWTEvent) line: 180
javax.swing.RepaintManager.scheduleProcessingRunnable(sun.awt.AppContext) line: 1369
javax.swing.RepaintManager.nativeAddDirtyRegion(sun.awt.AppContext, java.awt.Container, int, int, int, int) line: 548
javax.swing.SwingPaintEventDispatcher.createPaintEvent(java.awt.Component, int, int, int, int) line: 45
sun.awt.windows.WFramePeer(sun.awt.windows.WComponentPeer).postPaintIfNecessary(int, int, int, int) line: 741
sun.awt.windows.WFramePeer(sun.awt.windows.WComponentPeer).handlePaint(int, int, int, int) line: 736
sun.java2d.d3d.D3DScreenUpdateManager.repaintPeerTarget(sun.awt.windows.WComponentPeer) line: 274
sun.java2d.d3d.D3DScreenUpdateManager.createScreenSurface(sun.awt.Win32GraphicsConfig, sun.awt.windows.WComponentPeer, int, boolean) line: 175
...
sun.awt.windows.WToolkit.createFrame(java.awt.Frame) line: 383
javax.swing.JFrame(java.awt.Frame).addNotify() line: 460
javax.swing.JFrame(java.awt.Window).show() line: 859
javax.swing.JFrame(java.awt.Component).show(boolean) line: 1584
javax.swing.JFrame(java.awt.Component).setVisible(boolean) line: 1536
javax.swing.JFrame(java.awt.Window).setVisible(boolean) line: 842
Example.main(java.lang.String[]) line: 113
On the Mac, a call to PostEventQueue.postEvent(AWTEvent)
is made. Similarly on Windows, a call to java.awt.EventQueue.postEvent(java.awt.AWTEvent)
is made. Both eventually call EventQueue.initDispatchThread
.
As another example, consider the following main
method:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("Start!");
}
});
}
Using the Mac's JDK on the main
thread:
EventQueue.initDispatchThread() line: 906 [local variables unavailable]
EventQueue.postEventPrivate(AWTEvent) line: 227
EventQueue.postEvent(AWTEvent) line: 208
EventQueue.invokeLater(Runnable) line: 1048
SwingUtilities.invokeLater(Runnable) line: 1267
Example.main(String[]) line: 31
Using Oracle's JDK for Windows on the main
thread:
java.awt.EventQueue.initDispatchThread() line: 861
java.awt.EventQueue.postEventPrivate(java.awt.AWTEvent) line: 199
java.awt.EventQueue.postEvent(java.awt.AWTEvent) line: 180
java.awt.EventQueue.invokeLater(java.lang.Runnable) line: 999
javax.swing.SwingUtilities.invokeLater(java.lang.Runnable) line: 1267
The call to SwingUtilties.invokeLater
is responsible for starting the EDT. Here again, calls to EventQueue.postEvent(AWTEvent)
are made.
Not just any call to someSwingComponent.setVisible(true)
will start the EDT. For example, executing the following main
method does not create the AWT-Event-Queue-0
thread:
public static void main(String[] args) {
JLabel label = new JLabel();
label.setVisible(true);
}
Of course, there are many resources online about the EDT.
编辑:你们是对的;EDT 不会在启动时直接启动。我做了一些调试,这就是我发现的:
每当组件通过调用 Toolkit.getEventQueue() 请求访问事件队列时,都会延迟启动事件调度线程。这可以在调用 Component.show() 时完成(与 Component.setVisible() 相同),但也有其他调用可以触发此初始化,如 Component.repaint()。一旦获得对事件队列的引用,就可以使用 EventQueue.postEvent() 将作业添加到其中。此方法检查 EDT 是否存在,如果不存在,则使用 initDispatchThread() 创建它。
阻止它启动的唯一方法是使用“-Djava.awt.headless=true”标志以无头模式启动JVM(这会同时禁用AWT)。但这基本上是您可以与之进行的唯一低级交互。
组件的 setVisible 方法应始终在 EDT 上调用(就像您对 Swing / AWT 组件所做的任何其他修改一样)。您通过告诉 Java 在 EDT 上执行代码来使用 EDT。最简单的方法是使用 SwingUtilities.invokeLater()。这会安排您的 Thread(您的 Runnable 实现)从 EDT 执行。这是您作为开发人员应该与 EDT 进行的唯一一种交互。您不应该与 EDT 进行任何类型的低级交互,例如暂停或中止线程。
对于任何启动 EDT 的 AWT / Swing 容器,您是正确的 setVisible,非安全方式
初始线程是关于最安全的方式
如果所有事件都在当前 EDT 中完成,则 isDispatchThread 返回 false
在 isDispatchThread 返回 false 的情况下,任何 Swing 线程安全方法都可以激活 EDT,最安全的方法是从 invokeLater() 调用
测试代码
import java.awt.Color;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class IsThereEDT {
private ScheduledExecutorService scheduler;
private AccurateScheduledRunnable periodic;
private ScheduledFuture<?> periodicMonitor;
private int taskPeriod = 30;
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
private Date dateRun;
private JFrame frame1 = new JFrame("Frame 1");
public IsThereEDT() {
scheduler = Executors.newSingleThreadScheduledExecutor();
periodic = new AccurateScheduledRunnable() {
private final int ALLOWED_TARDINESS = 200;
private int countRun = 0;
private int countCalled = 0;
private int maxCalled = 10;
@Override
public void run() {
countCalled++;
if (countCalled < maxCalled) {
if (countCalled % 3 == 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("Push a new event to EDT");
frame1.getContentPane().setBackground(Color.red);
isThereReallyEDT();
}
});
} else {
if (this.getExecutionTime() < ALLOWED_TARDINESS) {
countRun++;
isThereReallyEDT(); // non on EDT
}
}
} else {
System.out.println("Terminating this madness");
System.exit(0);
}
}
};
periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.SECONDS);
periodic.setThreadMonitor(periodicMonitor);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
isThereReallyEDT();
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.getContentPane().add(new JLabel("Hello in frame 1"));
frame1.pack();
frame1.setLocation(100, 100);
frame1.setVisible(true);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame2 = new JFrame("Frame 2");
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.getContentPane().add(new JLabel("Hello in frame 2"));
frame2.pack();
frame2.setLocation(200, 200);
frame2.setVisible(true);
isThereReallyEDT();
}
});
}
private void isThereReallyEDT() {
dateRun = new java.util.Date();
System.out.println(" Time at : " + sdf.format(dateRun));
if (EventQueue.isDispatchThread()) {
System.out.println("EventQueue.isDispatchThread");
} else {
System.out.println("There isn't Live EventQueue.isDispatchThread, why any reason for that ");
}
if (SwingUtilities.isEventDispatchThread()) {
System.out.println("SwingUtilities.isEventDispatchThread");
} else {
System.out.println("There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that ");
}
System.out.println();
}
public static void main(String[] args) {
IsThereEDT isdt = new IsThereEDT();
}
}
abstract class AccurateScheduledRunnable implements Runnable {
private ScheduledFuture<?> thisThreadsMonitor;
public void setThreadMonitor(ScheduledFuture<?> monitor) {
this.thisThreadsMonitor = monitor;
}
protected long getExecutionTime() {
long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
return delay;
}
}