[펌글] Swing에서의 Thread 핸들링

By | 8월 15, 2008

효율성을 늘리고 복잡성을 줄이기 위해서 모든 Swing 컴포넌트는 thread-safe하지 않게 디자인되었다. 이는 간단하게 Swing 컴포넌트로의 모든 접근이 단일 쓰레드에서만 이루어져야한다는 의미이다. 이 쓰레드는 event-dispatch thread라고 불리며, 사용자가 직접 생성시키는 것은 아니다. 실행되고 있는 코드가 event-dispatch thread에 있는지 확실하지 않다면, EventQueue의 정적 isDispatchThread() 메소드를 통해 조사할 수 있다. 또는, SwingUtilities 클래스의 정적 isEventDispatchThread() 메소드를 통해서 조사할 수도 있다. isEventDispatchThread() 메소드는 isDispatchThread() 메소드의 프록시 역할을 한다.

event-dispatch thread의 태스크를 정확하게 실행하기 위해, Runnable 인터페이스를 구현하고 태스크를 EventQueue 클래스로 전달한다. event-dispatch thread 의 태스크를 실행해야하지만 결과값이 필요하지 않고 태스크가 언제 끝나던 상관없다면, EventQueuepublic static void invokeLater(Runnable runnable) 메소드를 사용하라. 그러나 태스크가 완료되어 값을 리턴하기 전에는 작업을 지속할 수 없다면, EventQueuepublic static void invokeAndWait(Runnable runnable) 메소드를 사용하기 바란다. invokeAndWait(Runnable runnable)를 이용하게되면 리턴값을 얻기 위한 코드를 추가해야한다(invokeAndWait() 메소드는 리턴되지 않는다.)

SwingUtilities 클래스에 익숙하다면 이 클래스가 invokeLater() 메소드와 invokeAndWait() 메소드도 갖고 있다는 것을 알 것이다. 그러나 이 두 메소드는 EventQueue 버전으로의 호출을 간단히 래핑해버리고 만다. 따라서 직접 EventQueue 버전을 호출하는 것이 낫다.

화면상에 보여지는 컴포넌트와 안보여지는 컴포넌트 모두, event-dispatch thread로부터 Swing 컴포넌트에 액세스해야한다. 화면상에 안보여지는 컴포넌트는 event-dispatch thread 대신 일반 쓰레드에서 액세스하는 것이 합리적으로 보일 수도 있다. 그러나, Swing GUI를 구축하면 리스너에게 통지되고(예. 속성 변화 이벤트, 원형 컴포넌트 추가시) 그 통지는 event-dispatch thread 상에 있으므로, Swing 컴포넌트는 event-dispatch thread에서 액세스하는 것이 언제나 가장 합리적이다.

event-dispatch thread로의 모든 액세스에 대한 이런 요구사항은 Swing 프로그램을 생성하는 것을 흥미롭게 만들어준다. 프로그램의 main() 메소드가 처음으로 하는 것이 Runnable 오브젝트 생성, JFrame생성, 그 프레임에 모든 컴포넌트 삽입하는 것이기 때문이다.

   Runnable runnable = new Runnable() {
     public void run() {
       // build screen
     }
   }
   EventQueue.invokeLater(runnable);

다음은 그런 프로그램의 중 하나이다. 프레임과 버튼을 생성하고, 버튼이 선택되면, "I was seleted"라는 메세지가 출력된다.

   import javax.swing.*;
   import java.awt.*;
   import java.awt.event.*;
   public class ButtonSample {
     public static void main(final String args[]) {
       Runnable runner = new Runnable() {
         public void run() {
           String title = args.length == 0 ?
             "Hello, World" : args[0];
           JFrame frame = new JFrame(title);
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           JButton button = new JButton("Select Me");
           // Define ActionListener
           ActionListener actionListener =
             new ActionListener() {
                public void actionPerformed(
                  ActionEvent actionEvent) {
               System.out.println("I was selected.");
             }
           };
           // Attach Listeners
           button.addActionListener(actionListener);
           frame.add(button, BorderLayout.SOUTH);
           frame.setSize(300, 100);
           frame.setVisible(true);
         }
       };
       EventQueue.invokeLater(runner);
     }
   }
Button Sample

프레임의 타이틀은 코멘드 라인을 통해 제공된다. Runnable 오브젝트가 또다른 클래스를 생성하기 때문에 메인메소드에 매개변수가 코멘드 라인 인수로 액세스하는 마지막임을 선언해야한다.

   public static void main(final String args[]) {

이를 생략한 채 이너클래스에서 args에 액세스하면, 컴파일 타임 에러 메시지를 얻게된다.

  ButtonSample.java:9: local variable args is accessed from
    within inner class; needs to be declared final
     String title = args.length == 0 ? "Hello, World" : args[0];
                      ^
  ButtonSample.java:9: local variable args is accessed from
    within inner class; needs to be declared final
     "Hello, World" : args[0];
                         ^
2 errors

Swing 인터페이스로 작업할 때 쓰레드 문제를 피하려면 모든 액세스가 event-dispatch thread를 통해 전달됨을 확실히 해야한다. 오랫동안 구동하는 태스크의 경우 새로운 쓰레드를 fork할 수 있다. invokeLater() 대신 invokeAndWait()를 사용한다면, 호출 메소드는 실행 중인 쓰레드가 블록되었다가 호출자에 제어를 넘겨준다. 다시 말해, event dispatch thread 대신 일반 쓰레드로부터의 invokeAndWait()만을 사용해야한다. 또한 invokeAndWait()를 사용하면, 실행이 호출 쓰레드로 리턴될 때 양 쓰레드가 모두 아는 곳으로부터 "리턴값"을 얻을 수 있다. 호출 쓰레드가 블록되어 있으므로 동기화는 필요하지 않다.

Swing에 쓰레드 사용하는 것에 대한 좀 더 자세한 정보는 Java Tutorial의 How to Use Threads를 참조하기 바란다. 이 문서에는 SwingWorker라고 불리는 비표준 헬퍼 클래스에 대한 설명이 포함되어 있다. worker 클래스를 사용한다면, 2000년 2월에 출시된 버전 3(SwingWorker 3)를 사용해야한다. 그 이전 버전에서는 버그가 생긴다.

- 출처 : http://www.sdnkorea.com/blog/190 -

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments