IT_Programming/Java

[펌] SWING에서의 쓰레드 핸들링

JJun ™ 2010. 4. 23. 16:52

=================================================================================================

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

=================================================================================================

 

효율성을 늘리고 복잡성을 줄이기 위해서 모든 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);
     }
   }

 

 

프레임의 타이틀은 코멘드 라인을 통해 제공된다. 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)를 사용해야한다.

그 이전 버전에서는 버그가 생긴다.

 

=================================================================================================

'IT_Programming > Java' 카테고리의 다른 글

XML Parsing 관련 예제 코드  (0) 2010.05.04
MediaTracker 사용하기  (0) 2010.05.04
[펌] Concurrency in Swing  (0) 2010.04.23
JavaDOC 사용하기  (0) 2010.03.15
JavaDoc 유틸리티  (0) 2010.03.15