torutkのブログ

ソフトウェア・エンジニアのブログ

JavaFX 2.0でアプリケーション作成(その7)

JavaFX 2.0でアプリケーション作成(その6) - torutkのブログの続きです。

Semaphoreクラスのacquireメソッドは、セマフォの許可数が1以上あれば、許可数を1減じてリターンしますが、許可数が0であれば、許可数が1以上になるまで(すなわちreleaseメソッドが呼ばれるまで)ブロックします。

JavaFXアプリケーション・スレッドでSemaphoreのacquireメソッドを呼び、ブロックされると、GUIが固まった状態になってしまいます。そこで、ブロックされるメソッドや処理に時間のかかるメソッドを呼ぶときは、JavaFXアプリケーション・スレッドとは別のスレッド(バックグラウンドスレッド)で実行します。

今回は、JavaFX 2アプリケーションでバックグラウンドスレッドの使い方を調べます。

ドキュメント

JavaFX公式サイトのドキュメントページに掲載されているドキュメントです。PDF版も用意されています。

このドキュメントによると、JavaFX 2.0ライブラリで用意されているバックグラウンドスレッド用のAPIは、javafx.concurrentパッケージのTaskクラスまたはServiceクラスとなります。どちらのクラスもabstractで、同パッケージのWorkerインタフェースを実装する形となっています。アプリケーション側でサブクラスを定義して使用します。

なお、Web上での情報では、Javaの標準機能でスレッドを生成し、javafx.application.PlatformクラスのrunLaterメソッドで別スレッドからJavaFX APIを呼ぶコーディングも見かけます。これは、SwingにおけるSwingUtilities.invokeLaterメソッドと同じ目的と思われます。

Taskクラス

Taskクラスはjava.util.concurrent.FutureTaskのサブクラスとして定義されています。
よって、最低限callメソッドを実装すれば使用できます。Taskをバックグラウンドスレッドで動かすためには、ふつうのスレッドを生成するのと同様、Threadクラスまたはjava.util.concurrentパッケージを使います。

ただし、callメソッドは別スレッドで実行するので、callメソッドの内部でJavaFXAPIを呼ぶことができません。別スレッドで実行した結果をJavaFXのシーングラフに反映させるには、Taskクラスに用意されているupdateProgress/updateMessage/updateTitleを使います。callメソッドでこの3つのメソッドのいずれかを呼ぶと、Taskの対応するプロパティの値が変化します。したがって、このプロパティの変化をシーングラフのいずれかにあらかじめバインドしておくことで、Taskの実行に応じて表示を変えるということです。

Taskクラスの注意点は、Taskクラスのインスタンスを生成し、いったん実行した後は、同じインスタンスを再度実行しようとすると例外を投げる点です。つまり、Taskクラスのインスタンスは1回だけしか実行できません。毎回インスタンスを生成する必要があります。

Serviceクラス

Serviceクラスは、最低限createTaskメソッドを実装して使用します。createTaskメソッドは、上述のTaskクラスのインスタンスを生成するメソッドで、実際に別スレッドで実行される処理はTaskクラスのcallメソッドとなります。

start/reset/restartメソッドを持ち、ServiceクラスのインスタンスはTaskのように1回使ったらおしまいという制約はありません*1

Platformクラス

javafx.application.Platformクラスには、staticメソッドrunLaterが定義されています。よって、SwingではおなじみのinvokeLaterと同様の記述をすることができます。

    Platform.runLater(new Runnable() {
        public void run() {
            // ...
        }
    });

JavaFX的にはあまり推奨する使い方ではないかもしれませんが、「プロパティのバインド」では実現が難しい場合に役立つと思われます。

次回

セマフォの獲得(ブロッキング呼び出し)を行うSemaphore.acquireメソッドを別スレッドで実行する機能を追加します。GUIとして、ボタンの追加、別スレッドが実行している間はProgressBarを表示させる予定です。

*1:createTaskメソッドで生成するTaskインスタンスについては使い回しできないかもしれません。調査予定