2010-09-03 77 views
9

我讀到構造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並在主線程中調用大量其他方法。我不明白美國東部時間到底在哪裏(它執行什麼代碼?)。 GridBagLayoutTester類的構造函數也從main方法調用,這意味着EDT沒有運行它。

總之

  1. 當在EDT正在啓動? (如果在運行此代碼時啓動所有EDT,JVM是否會啓動EDT以及主方法?)
  2. 按鈕的事件處理程序代碼是否在EDT上運行?

回答

12

代碼完美地工作,因爲您正在主線程中構建框架,之後EDT纔有機會與其交互。從技術上講,你不應該這樣做,但從技術上講,你可以在這個特定的情況下,因爲你不能與JFrame交互,直到它變得可見。

要知道的重點是Swing組件不是線程安全的。這意味着它們不能同時從多個線程修改。這是通過確保所有修改來自EDT來解決的。

EDT是一個致力於用戶交互的線程。用戶生成的任何事件始終在EDT上運行。任何用戶界面更新都在EDT上運行。例如,當您致電Component.repaint()時,您可以從任何線程調用此函數。這只是簡單地設置一個標記來標記組件需要繪畫,而EDT則會在下一個循環中執行。

EDT自動啓動,並與系統實施緊密相關。它在JVM中處理得很好。通常,它與處理用戶交互的窗口系統中的單個線程相關。當然,這是相當實施的依賴。好的是你不必擔心這一點。您只需知道 - 如果您與任何Swing組件交互,請在EDT上執行此操作。

同樣,還有一件事很重要。如果您打算對外部資源進行任何長時間處理或阻塞操作,並且您將針對用戶生成的事件執行此操作,則必須安排此操作在EDT外的自己的線程中運行。如果您未能做到這一點,則會導致用戶界面在等待長時間處理運行時阻塞。優秀的例子是從文件加載,從數據庫讀取數據或與網絡進行交互。您可以使用SwingUtilities.isEventDispatchThread()方法測試以查看您是否在EDT上(可用於創建可從任何線程調用的中性方法)。

這裏有兩個代碼片斷,我寫Swing編程處理的EDT時使用相當頻繁:

 
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); 
    } 
} 
+0

關於Component.repaint()我懷疑這只是「設置標誌」,它實際上排隊寫生活動(將然後由EDT處理)。 – jfpoilpret 2010-09-03 14:23:21

+1

模式是一樣的。您不需要知道爲了成功使用EDT而處理的內部處理方式。 – 2010-09-03 14:28:09

+0

所以frame.setVisible()調用在EDT上執行? – Stormshadow 2010-09-03 14:33:15

1

1)我不知道,如果在new JFramesetVisible但它初始化需求,這就是主要的方法結束(在主進程線程)不會終止進程。美國東部時間上午發射,並在阻止等待下一個事件的循環。

2)明確地說。該循環從OS接收事件,找到JButton並告訴它該事件已被觸發。該按鈕然後調用監聽器。美國東部時間發生的所有事情。

您可以查看您想要殺死進程(或關閉主窗口)以查找EDT終止位置時調用的Swing代碼......這可以給您一個線索(我會稍後再做!:)

3

正如其名稱所暗示的,事件調度線程每次需要處理事件時都會由Swing調用。

在您給出的示例中,當需要處理操作事件時,「測試」按鈕將自動調用actionPerformed方法。因此,您的actionPerformed方法的內容將由Event Dispatch Thread調用。

要回答你的最後兩個問題:

  • 的EDT自動啓動了Swing框架加載時。你不必關心啓動這個線程,JRE爲你處理這個任務。
  • 事件處理程序代碼由EDT運行。 Swing界面生成的所有事件都彙集起來,EDT負責執行它們。