본문 바로가기

Swing/Event Dispatched Thread

Java Event Dispatch Thread

자바 Event Dispatch Thread (EDT) 의 특성

자바 Swing 콤포넌트들은 대부분 다중 쓰레드로부터 안전하지 못한 특성을 가지고 있기 때문에 Swing 콤포넌트의 UI 상태를 변경하는 것은 Event Dispatch Thread 라는 단일 쓰레드 체계에 의해 관리되고 있으며 Swing 콤포넌트에서 이벤트가 발생할 때 Event Dispatch Thread에 의해서 이벤트 핸들러 메소드가 호출되어 실행되는 방법을 사용하고 있다. 

몇개의 Swing 콤포넌트들은 'Thread Safe' 한 특성을 가지고 있지만 대부분의 Swing 콤포넌트들은 Thread Safe 하지 못하기 때문에 반드시 Event Dispatch Thread에 의해서 호출되어 갱신되도록 해야 한다


개발자가 생성한 쓰레드에서 UI가 갱신되도록 코드를 작성한다면 대부분의 경우에는 제대로 작동할 수도 있지만 예기치 못한 오류를 발생할 수 있다.


Swing 콤포넌트는 하나의 Event Dispatch Thread 에 의해서 갱신되기 때문에 Event Dispatch Thread 가 코드실행을 종료할 때 비로소 화면이 갱신되는 효과를 볼 수 있다. Event Dispatch Thread 가 실행하는 코드 이외의 영역에서 Swing 콤포넌트를 갱신하려면 다음과 같은 방법을 사용하여 Event Dispatch Thread 에게 실행할 코드를 전달하는 방법을 사용해야 한다


EventQueue.invokeLater(), SwingUtilities.invokeLater() 를 사용하면 Event Dispatch Thread에게 실행할 코드를 전달할 수 있다


Event Dispatch Thread 이외의 영역에서 Swing 콤포넌트의 갱신이 필요하다면 다음과 같이 Runnable인스턴스의 run() 메소드 안에 해당코드를 정의하면 된다

java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {

                new ChildFrame().setVisible(true);

            }

        });


EDT가 실행하는 코드가 종료되어야만 화면의 Swing 콤포넌트가 갱신되는 효과가 나타나기 때문에 EDT 안에서 무한 루프를 실행한다면 화면이 멈춰버리는 현상을 보게 된다. 그러므로 EDT 가 실행할 코드는 가능한 짧게 구성할 필요가 있고, 화면의 갱신과 관련성이 없는 경우에는 일반 쓰레드로 생성하여 실행하면 된다. EDT가 코드를 실행하는 기간에는 UI가 반응을 하지 않기 때문에 화면이 일시적으로 멈추게 되는 현상을 볼 수 있으며 만약 EDT가 무한 루프를 실행한다면 화면이 완전히 멈춰버리고 전혀 반응을 보이지 않게 된다.


다음은 특정 버튼이 눌렸을 때 일반 쓰레드를 생성하고 그 쓰레드 안에서 무한 루프를 실행하는 예이다. 

만일 아래의 코드를 EDT에게 전달하기 위해 다음과 같이 작성했다면 EDT는 무한루프를 실행하고 종료되지 않으므로 화면을 갱신하지 못하고 프로그램이 멈춰버리는 현상을 경험하게 될 것이다


SwingUtilities.invokeLater(new Runnable() {

     @Override

     public void run() {

         try {

              ss = new ServerSocket(1234);

              while(true) {

                   System.out.println("서버 대기중...");

                   Socket socket = ss.accept();

               }

          } catch (Exception ex) {

               ex.printStackTrace();

          }

     }

});



아래의 코드처럼 화면의 갱신과 관련성이 없는 코드는 EDT를 사용하지 않고 일반 쓰레드로 생성하여 실행면 된다.


private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {                                         


        new Thread(){

            @Override

            public void run() {

                try {

                    ss = new ServerSocket(1234);

                    while(true) {

                        System.out.println("서버 대기중...");

                        Socket socket = ss.accept();

                    }

                } catch (Exception ex) {

                    ex.printStackTrace();

                }

            }

        }.start();

        

        new ChildFrame().setVisible(true);

        

        this.setVisible(false);

    }