kanizaのブログ

コンピュータ、ソフトウェア、映画、音楽関連や家族のことなど、思いついたことを書きます。

Swingでのマルチスレッド

JavaのSwingでのマルチスレッドなプログラミングをあれこれやった。僕はマルチスレッドプログラミングの知識が足りないなぁとしみじみ思う。

Swingにはシングルスレッドルールというのがあって、GUIコンポーネントはイベントディスパッチスレッドからのみアクセスすることになっている(他のGUIツールキットでもあると思う)。だからSwingアプリケーションではマルチスレッドプログラミングをしないという誤解があったりもするようだけど、時間のかかる処理は別スレッドでやらないと処理のあいだGUI全体が止まってしまうわけでむしろマルチスレッド重要。処理中にGUIが止まるアプリケーションはよろしくない。たとえば、ウェブブラウザで、あるページにアクセスにいったらページを読み込み終わるまですっかり表示が止まってるようだったらかなりよろしくないもんね。

Swingで別スレッドを使うには、イベントディスパッチスレッド(EDT)から別スレッドをstart()してEDTはそのまま進めてしまい、別スレッドの処理が終わったら(もしくは処理中に)そこからEDT側にGUIの更新処理を渡してやることになる。別スレッドからEDTに処理を渡すにはSwingUtilities.invokeLater(Runnable)あたりを使う。

別スレッドを生成したりinvokeLaterするRunnableを作ったりと、無名クラスが増えてコードがごちゃごちゃしてしまいがちなのだけど、一連の手順をカプセル化したSwingWorkerというクラスをSunが公開していて、それを使うとわりとシンプルに書ける。SwingWorkerはだいぶ前からあって、僕はcunstruct()とfinished()をオーバーライドするバージョンをしばらく使っていた。

SwingXとかJDK6にあるSwingWorkerはJDK5に入った並行処理ユーティリティの枠組みを使ったものになっている。前に見た時は「なんか作りが違うなー」と思っただけで手を出さなかったんだけど、今日はちょっとマジメに見てみた。doInBackground()で処理を書いて、done()でGUIを更新。処理の途中で更新したい場合はdoInBackground()内からpublish()したものをprocess()で受けとってごにょごにょやるということのようだ。

試しに使ってみたらたいへん具合が良い。前のSwingWorkerでは処理中に進捗状況を表示するというようなことを書くのが面倒だったからね。さらに、Genericsやら可変長引数のおかげで使いやすくなっている。というわけで古いSwingWorkerを使っていた部分をぜんぶ新SwingWorkerを使ったものに書き換えた。これまでSwingWorkerを使えなかった部分にも使えるようになってコードもシンプルに。めでたしめでたし。

JDK5の並行処理ユーティリティを活用すれば他の部分でもエレガントにバックグラウンドでの処理を書けてサクサク感あるGUIになるのかもしれんけど、まだ知識が足りんので説明を読んでもピンと来ないのが悲しいところ。ちゃんと勉強せずに手を出すと痛い目にあいそうでもある。でも避けては通れない道だと思うし勉強するしかないな。うむ。