いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

Swingスレッド処理-002(イベントディスパッチスレッドについて, Swingのスレッドポリシー, シングルスレッドポリシー, Event Dispatch Thread)


前回までの記事リンク


一度実体化(描画)されたコンポーネントは、それ以降状態を取得・変更しようとする場合は
イベントディスパッチスレッドからアクセスしないといけないのですが、ではどのタイミングから
イベントディスパッチスレッドからアクセスするようにしないといけないのでしょうか。


SDNのドキュメント Threads and Swing(ページへのリンクは前回の記事を参考願います)に書いてあるのですが
以下のメソッドが呼ばれてしまうとそれ以降はEDTからアクセスしなければならないようになります。

  • paint
  • setVisible(true)
  • show
  • pack


setVisible(true)が入っているように、可視のコンポーネントが対象となりますので
setVisible(false)の場合、つまり不可視のコンポーネントはEDT以外からでもアクセス可能ということに
なります。


また、明示的に子のコンポーネントのpaintなどを呼んでいなくてもトップレベルのウィンドウがpaintされれば
子のコンポーネントも可視化されます。


現在稼動しているスレッドがイベントディスパッチスレッドかどうかを確認するには、
javax.swing.SwingUtilitiesクラスの以下のメソッドを使用して調べられます。

SwingUtilities.isEventDispatchThread()


また通常イベントディスパッチスレッドは

AWT-Eventqueue-x(数字)

というスレッド名になっています。


また、イベントディスパッチスレッドは主にコンポーネントの描画とイベント処理の両方を担当します。
つまり、actionPerformedなどのイベントリスナはイベントディスパッチスレッドからコールされます。
なので、イベントリスナのメソッド内ではコンポーネントにそのままアクセスしても問題ありません。
以下のコードは、イベントディスパッチスレッドにて実行されているので安全です。

jButton.setText("Hehe");
jButton.addActionListener(new SafeAction());

class SafeAction extends AbstractAction{
    public void actionPerformed(ActionEvent evt){
        //
        // このメソッドはEDTにてコールされるのでコンポーネントにアクセス可能
        //
        jButton.setText("Hoge");
    }
}


逆に、以下のメソッドはイベントディスパッチスレッド以外からアクセスしているので安全ではありません。

jButton.setText("Hehe");
jButton.addActionListener(new NotSafeAction());

class NotSafeAction extends AbstractAction{
    public void actionPerformed(ActionEvent evt){
        //
        // 以下のコードは別スレッドからコンポーネントにアクセスしている
        //
        Thread t = new Thread(new Runnable(){
            public void run(){
                jButton.setText("Hoge");
            }
        });

        t.start();
    }
}


では、全てのイベント処理をリスナメソッドにて、直接実行したらいいのかというと
そういうわけにはいきません。時間のかかる処理を行う場合、それをイベントディスパッチ
スレッドにて処理してしまうとその間スレッドが処理のためにとられてしまうため
描画ができなくなるからです。つまりユーザから見るとGUIがハングアップしているように
見えてしまいます。

jButton.setText("Hehe");
jButton.addActionListener(new GuiBlockedAction());

class GuiBlockedAction extends AbstractAction{
    public void actionPerformed(ActionEvent evt){
        //
        // わざと時間がかかるようにしてみる
        //
        try{
            TimeUnit.SECONDS.sleep(10);
        }catch(InterruptedException ex){}
        
        jButton.setText("Hoge");
    }
}


上記のようなコードの場合は、ボタンを押した瞬間からGUIがブロックされます。
つまり、メニューも押せず、ボタンも押せず、さらにはXボタンにて終了もできません。
何故ならウィンドウの終了処理もイベントディスパッチスレッドで処理されるからです。


では、時間のかかる処理をどのようにして処理するのでしょうか。
それは次回に書きます。


================================
過去の記事については、以下のページからご参照下さい。