タイトルだけを見て「そもそも用途が違うじゃろがボケェ」と言うのはちょっと待って欲しい(笑)。
SwingのGUI部品
イマドキ使ってる人がどれほど居られるのかは分からないけれど、最近ちょっとJavaのGUIフレームワークである Swing に触れる機会があった。
SwingではGUI部品が thread-safe ではないため、GUI部品に対する操作を全て Event Dispatch Thread(以下EDT)で実行する必要がある。これは大昔のGUIアプリケーションで言うところの「イベントループ」をぐるぐる回しているスレッド、に相当する。即ち、GUI部品を操作している間はあらゆるGUI操作がブロックされることになるのだ。
脇道に逸れるのはここまでにしよう。それでは、EDT以外のスレッドからGUI部品を操作するにはどうすれば良いのか? その答えが javax.swing.SwingUtilities の2つのインスタンスメソッド「invokeAndWait」と「invokeLater」だ。どちらもRunnableなオブジェクトを引数に取り、そのRunnableオブジェクトの内容を実行してくれる。前者のメソッドは同期的に、後者のメソッドは非同期的に実行してくれる。詳細は公式のJavaDocを参照されたし。
疑問
とあるコードを読んでいると、以下のような箇所があった。
SwingUtilities.invokeAndWait(new Thread() { public void run() { // 何か処理 } });
invokeAndWaitが取る引数って java.lang.Runnable 型なのに、instantiationにオーバーヘッドのありそうな java.lang.Thread をわざわざ採用するのってバカなんじゃないの?!と思った。そこで、以下のような実験をすることにした。
実験
概要
- java.lang.Runnable または java.lang.Thread のどちらかによる anonymous class の instantiation を行う。
- 上記の instantiation の結果のオブジェクトを用いて、javax.swing.SwingUtilities#invokeAndWait を呼び出したり、invokeLater を呼び出したり、はたまた何もしなかったり(これは instantiation のみの所要時間の計測)。
を10万回×20セット実行し、その所要時間を比較する。
事前の予想
そりゃあもちろん、java.lang.Runnable を使った方が速いでしょ! …と思っていた。
実験に使ったコード
ここに貼ろうかと思ったけど、100行近いコードになったから、githubに置いた。
https://github.com/motooka/JavaTests/blob/master/test/SwingSpeedTest.java
結果その1:instantiationのみの速度比較
java.lang.Thread での anonymous class では10万回で平均118ms(ミリ秒)だったのに対し、java.lang.Runnable では平均1msに満たなかった。やはり java.lang.Thread の instantiation にはオーバーヘッドがあるということが確認できた。
今後の調査
- SwingUtilities#invokeAndWaitの中では何をやっているのか、ソースを読んでみたい。
- SwingUtilities#invokeLaterを大量に呼び出したときは死んでしまう訳だが、実行待ちのキューの長さを知ることはできるのかどうか調べてみたい。役には立たない気もするが。(そもそも大量に呼び出しちゃう時点で重大な設計ミスだろうし)
検証環境
- Hardware : MacBook Pro (Early 2011) 8GB RAM
- OS : Mac OS X 10.6.8
- Java : 1.6.0_29
- Development Environment : Eclipse (Helios Service Release 1)