스윙(Swing) 이벤트 처리 - Event Dispatch Thread

AWTAbstract Window Toolkit는 초기 자바의 GUI를 제공하던 툴킷이다. AWT의 자바와 OS를 연결하는 인터페이스는 이후에 주류가 되는 스윙Swing에서도 뼈대로 작동하고 있다. 따라서 이 글에서 설명하는 것은 AWT와 스윙이 구조적으로 공유하는 내용이다.

AWT은 단일 스레드 모델을 사용하여 작동하기 때문에 GUI의 상태를 변경하거나 이벤트 핸들링을 하는 코드는 모두 하나의 스레드에서 실행되어야 한다. 화면에 보이는 모든 GUI 객체와 각 객체의 상태 변경에 대한 스레드 안전성을 보장하는 것은 비효율적이기 때문에 대부분의 GUI 툴킷은 이러한 단일 스레드 모델을 사용한다.

이런 역활을 하는 스레드를 보통 UI 스레드라 하는데, AWT에서는 event dispatch(ing) thread 또는 EDT라 한다.12

따라서 일반적인 스윙 애플리케이션의 main 메소드는 GUI를 구동하는 Runnable 객체를 생성하여, event dispatch thread에서 실행되도록 요청하는 코드가 된다. 스윙의 event dispatch thread에 이런 실행을 요청하는 방법은 SwingUtilities 클래스의 invokeLater 메소드3invokeAndWait 메소드4가 있으며 실제 예는 다음과 같다.

import javax.swing.*;

public class SwingExample implements Runnable {
    private void createAndShowGUI() {
        JFrame f = new JFrame("Hello, world!");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }

    @Override
    public void run() {
        createAndShowGUI();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SwingExample());
    }
}

아래와 같이 애플리케이션 클래스가 Runnable 인터페이스를 상속하지 않고 익명anonymous 클래스를 사용하는 형태일 수도 있다. 중요한 것은 event dispatch thread 상에서 Swing 코드가 시작하고 실행되는 것이다.

public class SwingExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

invokeLater는 전달된 Runnable 객체가 event dispatch thread에서 실행되도록 큐에 넣고 곧바로 리턴한다(비동기적). invokeAndWait는 큐에 대기 중인 AWT 이벤트를 모두 처리하고 전달된 Runnable 객체의 실행을 끝낸 후 리턴한다(동기적).

따라서 invokeAndWait는 event dispatch thread 상에서 호출하면 교착 상태에 빠지게 된다. SwingUtilities에는 현재 스레드가 EDT인지 검사할 수 있는 isDispatchThread 메소드가 있기도 하지만, 구조적으로 이런 상황이 발생하지 않도록 해야 한다.

스윙은 AWT 기반의 구현체이기 때문에 이 글에서 소개한 SwingUtilities의 메소드들이 실제로는 java.awt.EventQueue 클래스의 동일한 메소드를 호출하도록 구현되어 있다. 이 클래스가 EDT의 이벤트 큐를 관리한다.

UI의 작동을 멈추게 할 수 있는 긴 작업은 다른 스레드에서 마친 후, UI를 갱신하는 코드만 Runnable 객체와 invokeLater를 통해 EDT에서 실행되도록 하는 것이 일반적인 구성이다. 실행 중인 사용자 스레드가 모두 종료되어야 자바 VM이 종료되기 때문에, 위 프로그램은 스윙 GUI가 완전히 실행을 마친 후 종료될 것이다.