Async in JavaFX 1.2 Part 2

前回の日記JavaFX 1.2 になって一新された非同期 API を試してみましたが....
いくつか気になった点があったので更に試してみました。

気になった点とは...

  • 非同期処理のロジックを JavaFX側 でコーディングできないか
  • RunnableFuture の進捗状況をどのように JavaTaskBase 側に渡すのか ( 前回のコメント 参照 )

です。

では、まずは 『非同期処理のロジックを JavaFX でコーディングできないか』 から...
非同期処理のロジックは JavaFX ではなく、Javaで実装しなくてはならないようですが、JavaFX を使っているのに、非同期処理の度に Java のコーディングをするのも ちょっとイマイチな感じ...

と言うことで...
前回のサンプルを コールバック関数 action を使ってちょっと改善してみました。
以下のようにすれば、JavaFX で実装した action 関数を Java に渡して非同期で実行できるようになるのです。
関数を変数と同様に扱えるという JavaFX の特徴をちょっと使ってみました。
とっても簡単なコードですが かなり便利...

しかし こんな簡単なコードの裏にも とてつもない落とし穴が潜んでいるのです。
気をつけないと 思いっきりハマります。私のように...

既にご存知の方もいるかもしれませんが、実は 以下のコード...
LongLongTask の action で Node を変更すると、恐ろしいことに スレッド がデッドロックするのです。
こうなってしまうと、アプリケーションは完全に固まってしまい 強制終了するしかありません。

しかし解決策はとても簡単!!
Node の変更は LongLongTask の onStart, onDone で行おう!!

Main.run(String[]) 及び LongLongTask の action, onStart, onDone は以下のスレッドで実行されています。

[Main.run] Thread[AWT-EventQueue-0,6,main]
[action  ] Thread[task.1,6,task thread pool]
[onStart ] Thread[AWT-EventQueue-0,6,main]
[onDone  ] Thread[AWT-EventQueue-0,6,main]

見て解かるように onStart と onDone は Main.run(String[]) と同じ EDT (Event Dispatch Thread) で実行されています。 当たり前のことですが 同じスレッド (つまり シングルスレッド) で処理している限り デッドロックすることはありません。

更に今回試してみて解かったことですが...
JavaFX 1.1 では 非同期処理を行うスレッドはリソースが許す限り無制限?に立ち上がりましたが、JavaFX 1.2 では Task がプールされるようになり、そのプールされた Task を処理するワーカースレッドの数は 最大8個までになったようです。この数を変更できるかどうかは不明です。WEBアプリと違ってクライアントアプリなら8個もあれば十分ということなのでしょうか...

LongLongTaskImpl.java

import javafx.async.RunnableFuture;

public class LongLongTaskImpl implements RunnableFuture {

    private  Runnable runnable;

    public LongLongTaskImpl(Runnable runnable) {
        this.runnable = runnable;
    }

    @Override
    public void run() {
        runnable.run();
    }
}

LongLongTask.fx

import java.lang.Runnable;
import javafx.async.JavaTaskBase;

public class LongLongTask extends JavaTaskBase {

    public-init var action: function():Void;

    override function create() {
        new LongLongTaskImpl(Runnable {
            override function run() {
                action();
            }
        });
    }
}

Main.fx の一部 変更した箇所

Timeline {
    repeatCount: Timeline.INDEFINITE
    autoReverse: true
    keyFrames: [
        KeyFrame {
            time: 0s
            action: function() {
                var task: LongLongTask;
                task = LongLongTask {
                    var _result: java.util.Date;
                    action: function() {
                        try {
                            java.lang.Thread.sleep(10000);
                        } catch (ex: java.lang.InterruptedException) {}
                        _result = new java.util.Date();
                    }
                    onDone: function() : Void {
                        result = _result;
                    }
                };
                task.start();	
            }
        },
        KeyFrame {
            time: 5s
        }
    ]
}.play();

ということで...
ちょっと書くのも疲れてきたので今回はここまで...
次回 『RunnableFuture の進捗状況をどのように JavaTaskBase 側に渡すのか』 へ続く...