IT_Programming/Java

[펌] Concurrency in Swing

JJun ™ 2010. 4. 23. 16:42

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

출처: http://blog.naver.com/ugigi/70033061798

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

 

출처 : http://java.sun.com/docs/books/tutorial/uiswing/concurrency/initial.html

 

Initial Threads

 

모든 프로그램은 어플리케이션 로직이 시작하는 스레드의 집합을 가지고 있다. 일반적인 프로그램에서는

그러한 스레드가 단 하나 존재한다: 해당 스레드는 프로그램 클래스의 main 메소드를 호출한다.

애플릿에서 애플릿 객체를 구성하고 init와 start 메소드를 호출하는 초기화 스레드는 하나이다;

이러한 작업은 단일 스레드에서 일어날 것이다, 또는 자바 플랫폼 구현에서 따라서 하나 또는

세개의 다른 스레드에서 일어날 수도 있다. 이번 레슨에서 이러한 스레드를 초기화 스레드(initial threads)

라고 부를 것이다.

 

스윙 프로그램에서, 초기화 스레드는 많은 역할을 하지 않는다. 가장 중요한 작업은 GUI를 초기화하고

이벤트 디스패치 스레드에서 실행되는 객체를 스케줄하는 Runnable 객체를 생성하는 것이다.

GUI가 한번 생성되고 나면, 프로그램은 주로 GUI 이벤트에 의해서 처리되고 각 이벤트의 결과로 이벤트

디스패치 스레드에서 짧은 작업들이 실행된다. 어플리케이션 코드는 이벤트 디스패치 스레드 (만약 작업이

빠르게 완료되면, 이벤트 프로세싱과 인터페이스 하지 않는다) 또는 worker 스레드에서 추가적인 작업을

스케줄할 수 있다.

 

초기화 스레드는 javax.swing.SwingUtilities.invokeLater 또는 javax.swing.SwingUtilities.invokeAndWait 를 호출함으로서 GUI 생성 작업을 스케줄한다. 두개의 메소드 모두 하나의 매개변수를 가진다: 새로운 작업을 정의하는 Runnable 객체이다. 두 메소드의 차이점은 그들의 이름으로 알 수 있다 : invokeLater 메소드는

단순히 작업을 스케줄하고 리턴한다; invokeAndWait 메소드는 작업을 완료한 후에 리턴한다.

 

Swing tutorial을 통해서 다음의 예제를 볼 수 있다.

 

SwingUtilities.invokeLater(new Runnable() {

    public void run() {

        createAndShowGUI();

    }

}

 

애플릿에서, GUI 생성 작업은 invokeAndWait를 사용해서 init 메소드를 통해서 실행되어야 한다; 그렇지 않으면, init 메소드는 GUI가 생성되기 전에 리턴될 것이다, 이것은 애플릿을 실행하는 웹 브라우저에 문제를 발생할 수 있다. 다른 종류의 프로그램에서는 GUI 생성 작업을 스케줄 하는 것은 보통 초기화 스레드에서 수행하는 마지막 작업이다, 그래서 invokeLater를 사용하든 invokeAndWait를 사용하든 문제가 되지 않는다.

 

왜 초기화 스레드가 간단하게 GUI를 스스로 생성하지 않는 것인가? 왜냐하면 스윙 컴포넌트를 생성하거나 상호작용하는 거의 모든 코드는 이벤트 디스패치 스레드에서 실행되어야 하기 때문이다. 이러한 제한 사항은 다음의 섹션에서 나중에 다룬다.

 

출처 : http://java.sun.com/docs/books/tutorial/uiswing/concurrency/dispatch.html

 

Event Dispatch Thread

 

스윙 이벤트 핸들링 코드는 이벤트 디스패치 스레드라는 특별한 스레드에서 실행된다. 스윙 메소드를

호출하는 대부분의 코드 역시 이벤트 디스패치 스레드에서 실행된다. 대부분의 스윙 객체의 메소드는

"thread safe"하지 않기 때문에 이것은 필요하다: 다중 스레드로부터 스윙 객체 메소드를 호출하는 것은 thread interference 또는 memory consistency errors의 위험이 있다. 몇가지 스윙 컴포넌트 메소드는

API 규약에 "thread safe"라고 명시되어 있다; 이러한 메소드는 어떠한 스레드에서도 안전하게 호출된다.

모든 다른 스윙 컴포넌트 메소드는 이벤트  디스패치 스레드로부터 호출되어야만 한다.

이러한 룰을 무시한 프로그램은 대부분 정확하게 동작하다가 어떠한 상황에서 재연하기 어려운 예측하지

하지 않은 에러를 발생할 것이다.

 

A note on thread safety: 자바 플랫폼의 중요한 부분에서 thread safe 하지 않다는 것은 이상하게 보일 것이다. thread-safe한 GUI 라이브러리를 생성하려는 시도는 몇가지 기본적인 문제점에 당면하게 되었다. 보다 자세한 사항은 Graham Hamilton의 블로그를 참조하라: MultiThreaded toolkits: A failed dream?

 

이벤트 디스패치 스레드에서 실행되는 작은 작업의 나열하는 코드를 생각하는 것이 좋다. 대부분의 작업은 ActionListener.actionPerformed와 같은 이벤트 핸들링 메소드를 호출하는 것이다. 다른 작업은 invokeLater 또는 invokeAndWait를 사용해서 어플리케이션 코드에 의해 스케줄 될 수 있다. 이벤트 디스패치 스레드에서 실행되는 작업은 반드시 빠르게 종료되어야 한다; 그렇지 않으면, 처리되지 않는 이벤트가 back up 되고 UI가 즉각 반응하지 않을 것이다.

 

코드가 이벤트 디스패치 스레드에서 실행되는지 확인할 필요가 있다면, javax,swing,SwingUtilites.isEventDispatchThread를 호출한다.

 

출처 : http://java.sun.com/docs/books/tutorial/uiswing/concurrency/worker.html

 

Worker Threads and SwingWorker

 

스윙 프로그램이 긴 시간이 걸리는 작업을 실행할 필요가 있으면, background thread로 알려진 woker threads 중의 하나를 사용한다. worker thread에서 실행되는 각 작업은 javax.swing.SwingWoker의 인스턴스에 의해 표현된다. SwingWorker는 추상 클래스이다; SwingWorker 객체를 생성하기 위해서는 서브클래스를 정의해야만 한다; 아주 간단한 SwingWorker 객체를 생성하기 위해서는 익명의 내부 클래스를 쓰는 것이 유용하다.

 

SwingWorker는 몇가지 communication과 control 기능을 제공한다:

 

. SwingWorker 서브클래스는 done 메소드를 정의할 수 있다, 이것은 background 작업이 종료되면

  이벤트 디스패치 스레드에서 자동으로 호출된다.

 

. SwingWoker는 java.util.concurrent.Future를 구현한다. 이 인터페이스는 background 작업이

  다른 스레드에게 값을 리턴할 수 있는 기능을 제공한다. background 작업을 취소하고 background 작업이

  종료되거나 취소되었을 때 복원하는 기능을 제공한다.

 

. background 작업은 SwingWorker.publish를 호출함으로서 intermediate 결과를 제공할 수 있다,

  (이벤트 디스패치 스레드로부터 호출되는 SwingWorker.process에 의해 야기되는...)

 

. background 작업은 bound 프로퍼티를 정의할 수 있다. 이벤트 디스패치 스레드에서 호출되는

  이벤트 핸들링 메소드로부터 발생하는 트리거 이벤트로 이러한 프로퍼티를 변경할 수 있다.

 

이러한 기능은 다음의 서브섹션에서 다룬다.

 

Note : javax.swing.SwingWorker 클래스는 Java SE 6에 추가된 클래스이다.

         이전 버전에서는 SwingWorker라는 다른 클래스가 동일한 목적으로 많이 사용되었다.

         예전의 SwingWorker 클래스는 Java platform 규약의 일부가 아니었고 JDK의 일부로 제공되지도

         않았다.

 

javax.swing.SwingWorker는 완전히 새로운 클래스이다. 기능은 예전의 SwingWorker의 완전한 superset

이 아니다. 두 클래스의 메소드들은 동일한 기능을 가지고 있지만 동일한 이름은 아니다. 또한 예전의 SwingWorker 클래스는 재사용가능하지만 새로운 클래스는 각각의 새로은 background 작업을 필요로한다.

 

자바 튜토리얼을 통틀어서, SwingWorker는 javax.swing.SwingWorker를 뜻한다.

 

Simple Background Tasks

 

아주 간단하지만 일정 시간이 소요될 가능성이 있는 작업부터 시작하자. TumleItem 애플릿은 애니매이션에 사용되는 여러장의 그래픽 파일을 로드한다. 그래픽 파일이 초기화 스레드에서 로드되면 GUI가 화면에 나타나기 전에 지연될 것이다. 그래픽 파일이 이벤트 디스패치 스레드에서 로드되면, GUI는 임시적으로 즉각반응하지 않을 것이다.

 

이러한 문제를 제거하기 위해서, TumbleItem은 초기화 스레드에서 SwingWorker의 인스턴스를 생성하고 실행한다. worker 스레드에서 실행되는 객체의 doInBackground 메소드는 ImageIcon 배열으로 이미지를 로드하고 배열의 참조 변수를 리턴한다. 이벤트 디스패치 스레드에서 실행되는 don 메소드는 참조 변수를 얻기 위해 get을 호출하고 imgs라는 이름의 애플릿 클래스 필드에 할당한다. 이렇게 함으로서 TumbleItem은 GUI를 즉시 구성할 수 있고 이미지가 로딩되기를 기다릴 필요가 없다.

 

SwingWorker 객체를 정의하고 실행하는 코드는 다음과 같다.

 

SwingWorker worker = new  SwingWorker<ImageIcon[], Void>() {

    @Override

    public ImageIcon[] doInBackground() {

        final ImageIcon[] innerImgs = new ImageIcon[nimgs];

        for (int i = 0; i < nimgs; i++)

            innerImgs[i] = loadImage(i + 1);

        return innerImgs;

    }

 

    @Override

    public void done() {

        // Remove the "Loading images" label.

        animator.removeAll();

        loopslot = -1;

        try {

            imgs = get();

        } catch (InterruptedException ignore) {}

        catch (java.util.concurrent.ExecutionException e) {

            String why = null;

            Throwable cause = e.getCause();

            if (cause != null) why = cause.getMessage();

            else why = e.getMessage();

            System.err.println("Error retrieving file : " + why);

        }

    }

};

 

SwingWorker의 모든 concrete 서브클래스는 doInBackground를 구현한다; done을 구현하는 것은 옵션이다.

 

SwingWorker는 두가지 타입의 파라미터를 가지는 일반 클래스인 것을 주의하라. 첫번째 파라미터는 doInBackground 타입을 리턴하는 파라미터이다, get 메소르를 위한 것이다, doInBackground에 의해서 리턴되는 객체를 얻기 위해서 다른 스레드에 의해 호출된다. SwingWorker의 두번째 타입 파라미터는 background 작업이 여전히 실행 중인 동안 리턴되는 중간 결과의 타입을 지정한다. 예제가 중간 결과를 리턴하지 않으면 void가 사용된다.

 

imgs를 설정하는 코드가 불필요하게 복잡해 보일 것이다. doInBackground가 객체를 리턴하고 객체를 얻기 위해서 done을 사용하게 했을까? doInBackground가 imgs를 직접 설정하지 않는 것일까? 문제는 worker 스레드에서 생성된 imgs객체를 참조하고 이벤트 디스패치 스레드에서 사용된다는 것이다. 이러한 방법으로 객체를 공유하면 하나의 스레드에서의 변경이 다른 스레드에서 알수 있도록 만들어야 한다. 이것을 보장하기 위해서 get을 사용한다, 왜냐면 get을 사용하면 imgs를 생성하는 코드와 사용하는 코드 간의 happens before 관계를 생성하기 때문ㅇ니다. happens before 관계에 대한 자세한 사항은 Concurrency 레슨의 Memory Consistency Errors를 참조하라.

 

doInBackground에 의해 리턴되는 객체를 얻는 두가지 방법이 있다.

 

. SwingWorker.get를 파라미터 없이 호출한다. background 작업이 종료되지 않으면 get은 종료 될때까지

  블럭시킨다.

 

. 타임아웃을 파라미터로 SwingWorker.get을 호출한다. background 작업이 종료되지 않으면,

  get은 종료 될때까지 블럭시킨다 - 타임 아웃이 만료되거나 java.util.concurrent.TimeoutException을

  throw 하면 이벤트 디스패치 스레드에서 get을 호출하는 것은 조심해야 한다; get이 리턴하기 전까지

  GUI 이벤트는 처리되지 않고 GUI는 "frozen" 상태로 있게 된다. background 작업이 완료되거나 완료 후 

  닫혔는지 확인하지 않으면 파라미터 없이 get을 호출하지 말라.

 

Tumble 예제에 대한 자세한 사항은 Using Other Swing Features 레슨의 How to Use Swing Timers를

참조하라.

 

[출처] [Swing]Concurrency in Swing|작성자 네이밍

 

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

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

MediaTracker 사용하기  (0) 2010.05.04
[펌] SWING에서의 쓰레드 핸들링  (0) 2010.04.23
JavaDOC 사용하기  (0) 2010.03.15
JavaDoc 유틸리티  (0) 2010.03.15
Finalize Guardian Idiom  (0) 2010.02.25