我读到构造Swing组件和处理事件的所有代码必须由Event Dispatch Thread运行.我了解如何通过使用SwingUtilities.invokeLater()方法来实现.考虑以下代码,其中GUI初始化在main方法本身完成
public class GridBagLayoutTester extends JPanel implements ActionListener { public GridBagLayoutTester() { setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); JButton button = new JButton("Testing"); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.WEST; gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; button.addActionListener(this); add(button,gbc); } public void actionPerformed(ActionEvent e) { System.out.println("event handler code"); } public static void main(String[] args) { JFrame frame = new JFrame("GridBagLayoutDemo"); frame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE); Container contentPane = frame.getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(new GridBagLayoutTester(),BorderLayout.CENTER); frame.setSize(800,600); frame.pack(); frame.setVisible(true); System.out.println("Exiting"); } }
这段代码如何正常工作?我们正在构建JFrame并在主线程中调用主机的其他方法.我不明白这里EDT在哪里(它执行什么代码?). GridBagLayoutTester类的构造函数也是从main方法调用的,这意味着EDT没有运行它.
简而言之
美国东部时间是什么时候开始? (如果在运行此代码的同时启动EDT时,JVM是如何启动EDT的)?
>按钮的事件处理程序代码是否在EDT上运行?
解决方法
代码的工作原理很好,因为您正在构建主线程中的框架,在EDT有机会与之进行交互之前.在技术上,你不应该这样做,但技术上你可以在这种具体情况下,因为你不能与JFrame交互,直到它变得可见.
要了解的主要方面是Swing组件不是线程安全的.这意味着它们不能同时被多个线程修改.这通过确保所有修改来自EDT来解决.
EDT是一个致力于用户交互的线程.从用户生成的任何事件总是在EDT上运行.任何在EDT上运行的用户界面更新.例如,当您调用Component.repaint()时,可以从任何线程调用.这只需要设置一个标志,将组件标记为需要油漆,而EDT在下一个循环中进行操作.
EDT自动启动,并且与系统实现相当紧密.它在JVM内处理得很好.通常,它与处理用户交互的窗口系统中的单个线程相关.当然,这是非常依赖于实现.好的是,你不用担心这个.您只需要知道 – 如果您与任何Swing组件交互,请在EDT上执行.
同样,还有另外一件事情很重要.如果您要对外部资源进行任何长时间处理或阻塞,并且您将要响应用户生成的事件来执行此操作,则必须安排在自己的线程中运行EDT.如果您无法执行此操作,则会在等待长时间处理运行时导致用户界面被阻止.优秀的示例是从文件加载,从数据库读取,或与网络交互.您可以使用SwingUtilities.isEventDispatchThread()方法来测试您是否在EDT上(可用于创建可以从任何线程调用的中性方法).
下面是两个代码片段,我在编写处理EDT的Swing编程时经常使用:
void executeOffEDT() { if (SwingUtilities.isEventDispatchThread()) { Runnable r = new Runnable() { @Override public void run() { OutsideClass.this.executeOffEDTInternal(); } }; new Thread(r).start(); } else { this.executeOffEDTInternal(); } } void executeOnEDT() { if (SwingUtilities.isEventDispatchThread()) { this.executeOnEDTInternal(); } else { Runnable r = new Runnable() { @Override public void run() { OutsideClass.this.executeOnEDTInternal(); } }; SwingUtilities.invokeLater(r); } }