2010-09-03 11 views
9

Przeczytałem, że cały kod, który tworzy komponenty Swing i obsługuje zdarzenia, musi być uruchamiany przez wątek wywołania zdarzenia. Rozumiem, w jaki sposób jest to realizowane przy użyciu metody SwingUtilities.invokeLater(). Rozważmy następujący kod gdzie inicjalizacji GUI odbywa się w sposób sam mainGdzie jest wywoływany wątek wywołania zdarzenia?

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"); 
    } 
} 

Jak to jest, że ten kod działa doskonale? Budujemy JFrame i wywołujemy wiele innych metod w głównym wątku. Nie rozumiem, gdzie dokładnie pojawia się tu EDT (jaki kod wykonuje?). Konstruktor klasy GridBagLayoutTester jest również wywoływany z metody main, co oznacza, że ​​EDT go nie uruchamia.

W skrócie

  1. Kiedy jest EDT czym zaczęło? (czy JVM uruchamia EDT wraz z główną metodą, jeśli w ogóle EDT jest uruchamiany podczas uruchamiania tego kodu?)
  2. Czy kod obsługi zdarzenia dla przycisku działa na EDT?

Odpowiedz

12

Kod działa doskonale, ponieważ konstruujesz ramkę w głównym wątku, zanim EDT ma możliwość interakcji z nią. Technicznie nie powinieneś tego robić nigdy, ale technicznie możesz w tej specyficznej sytuacji, ponieważ nie możesz wchodzić w interakcje z JFrame, dopóki nie stanie się widoczny.

Najważniejsze, aby wiedzieć, że komponenty Swing nie są bezpieczne dla wątków. Oznacza to, że nie można ich modyfikować z więcej niż jednego wątku w tym samym czasie. Jest to rozwiązywane przez zapewnienie, że wszystkie modyfikacje pochodzą z EDT.

EDT to wątek poświęcony interakcji użytkownika. Wszelkie zdarzenia generowane przez użytkownika są zawsze uruchamiane na EDT. Wszelkie aktualizacje interfejsu użytkownika są uruchamiane w EDT. Na przykład, dzwoniąc pod numer Component.repaint(), możesz wywołać to z dowolnego wątku. To po prostu ustawia flagę, aby oznaczyć element jako wymagający farby, a EDT robi to w następnym cyklu.

EDT uruchamia się automatycznie i jest ściśle związany z implementacją systemu. Jest obsługiwany również w ramach JVM. Zazwyczaj jest to skorelowane z pojedynczym wątkiem w systemie okienkowym, który obsługuje interakcje użytkownika. Oczywiście jest to zależne od implementacji. Fajną rzeczą jest to, że nie musisz się o to martwić. Trzeba tylko wiedzieć - jeśli wchodzisz w interakcje z dowolnymi komponentami Swing, zrób to na EDT.

Tak samo ważna jest jeszcze jedna rzecz. Jeśli zamierzasz wykonać długoterminowe przetwarzanie lub blokowanie zewnętrznego zasobu, a zrobisz to w odpowiedzi na zdarzenie wygenerowane przez użytkownika, musisz zaplanować, aby uruchamiał się we własnym wątku poza EDT. Jeśli tego nie zrobisz, spowodujesz blokowanie interfejsu użytkownika podczas oczekiwania na przetwarzanie długotrwałe. Doskonałe przykłady to ładowanie z plików, czytanie z bazy danych lub interakcja z siecią. Możesz sprawdzić, czy jesteś na EDT (przydatne do tworzenia neutralnych metod, które można wywołać z dowolnego wątku) za pomocą metody SwingUtilities.isEventDispatchThread().

Oto dwa fragmenty kodu, które używam dość często podczas pisania programowania Swing czynienia z 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

chodzi Component.repaint() Wątpię, to tylko „ustawia flagę”, to faktycznie kolejki wydarzenie malarstwa (który zostanie przetworzony przez EDT). – jfpoilpret

+1

Wzór jest taki sam. Nie musisz znać wewnętrznych cech tego sposobu, w jaki jest to obsługiwane, aby z powodzeniem używać EDT. –

+0

, więc wywołanie frame.setVisible() jest wykonywane na EDT? – Stormshadow

1

1) Nie wiem, czy w new JFrame lub w setVisible ale zainicjować na żądanie i to koniec głównej metody (ponad głównego wątku procesu) nie zakończyć proces. EDT został uruchomiony i jest zablokowany w oczekiwaniu na następne wydarzenie.

2) Definitively. Pętla ta otrzymuje od OS zdarzenia, znajduje JButton i informuje go, że zdarzenie zostało wywołane. Następnie przycisk wywołuje słuchaczy. Wszystko to dzieje się w EDT.

Możesz przejrzeć kod Swing, który wywołujesz, gdy chcesz zabić proces (lub zamknąć okno główne), aby sprawdzić, gdzie kończy się EDT ... co może dać ci wskazówkę (zrobię to później ! :)

3

Wątek wysyłki zdarzeń, jak sama nazwa wskazuje, jest wywoływany przez Swing za każdym razem, gdy zdarzenie wymaga przetworzenia.

W podanym przykładzie przycisk "Testowanie" automatycznie wywoła metodę actionPerformed, gdy konieczne będzie przetworzenie zdarzenia akcji. Tak więc zawartość metody actionPerformed zostanie wywołana przez wątek Dispatch zdarzenia.

Aby odpowiedzieć na dwa ostatnie pytania:

  • EDT jest uruchamiany automatycznie po załadowaniu ramowe Swing. Nie musisz dbać o uruchomienie tego wątku, JRE obsługuje to zadanie dla ciebie.
  • Kod obsługi zdarzeń jest uruchamiany przez EDT. Wszystkie zdarzenia generowane przez interfejs Swing są łączone i EDT odpowiada za ich wykonanie.
Powiązane problemy