SwingUtilitiesの利用
ここまで述べたスレッドの利用方法は、GUIを使わない場合は問題ありません。しかし、Swingのコンポーネントなどを利用する場合には、ちょっとした注意が必要です。
実は、Swingのコンポーネントは複数のスレッドから同時にアクセスすることを考えて作られていません。先ほどの様に、別スレッドからコンポーネントを操作する手法は望ましくないのです。
Swingコンポーネントをメインスレッド以外から操作する場合には、Swing関係のユーティリティ機能をまとめたSwingUtilitiesクラスから、invokeLaterメソッドを使って操作することが推奨されています。例えばrunメソッドをこんな具合にしてやるのです。
public void run() { while(roop_flag){ try { Thread.sleep(1000); } catch(InterruptedException e){ e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable(){ public void run(){ setTime(); } }); } }
runメソッドの中から、さらにRunnableのrunメソッドを実行してるのでちょっと分かりにくいのですが、SwingUtilities.invokeLaterというメソッドを呼び出し、この引数内にRunnableを無名クラスとして組み込んでいることが分かるでしょう。そして、そのrunメソッドでsetTimeを呼び出しています。
このSwingUtilities.invokeLaterは、イベント処理関係のスレッドが一通り終了したところでこのスレッドを安全に実行してくれるメソッドです。Swingのコンポーネントの表示を更新するような処理は、このようにして安全にスレッドを実行するようにしておきます。
注意しておきたいのは、このループを使ってエンドレスで実行するrun自体をSwingUtilities.invokeLaterからまるごと実行してはまずい、という点です。このinvokeLaterは、Swingのイベント関係の処理を行うスレッド(イベントディスパッチスレッドと呼ばれる特別なスレッドです)に、必要なrunの処理をはめ込んで実行します。
このため、サンプルの時計のように、エンドレスで実行され続ける処理をここに入れてしまうと、その処理でこのスレッドが占有され、GUIの操作ができなくなってしまいます。
あくまで、コンポーネントを更新する必要最低限の操作部分だけをinvokeLaterで実行させるようにしてください。
実はこのSwingUtilities.invokeLaterは、Swingを利用する上で非常に重要なものなのです。今まで、Swingのコンポーネントを動かす際は、当たり前のように以下の要領でJFrameを実行していました。
public static void main(String args[]) { Sample sample1 = new Sample(); sample1.setBounds(100,100,300,200); sample1.setVisible(true); }
これも、Swingのコンポーネントを操作するスレッドは安全に実行できるように注意するという観点からすれば、実はあまりいいやり方ではないのです。本来であれば以下のようにすべきです。
public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable(){ public void run(){ Sample sample1 = new Sample(); sample1.setBounds(100,100,300,200); sample1.setVisible(true); } }); }
Swingコンポーネントを別スレッドで実行する際の基本として、SwingUtilities.invokeLaterを使ってスレッドを実行するというやり方は覚えておくとよいでしょう。
