JavaFX Thread.join()がGUIスレッドをブロックする

JavaFXでThreadを使用した際にハマったポイントを記載します。
Threadの処理完了をjoinで待つとその間GUIが再描画されなかったので、原因と対応策について調べました。調べた内容を忘れないように備忘録に残したいと思います。

環境

  • CentOS リリース 6.10 (Final)
  • カーネル Linux 2.6.32-754.el6.x86_64
  • java version 1.8.0_241
  • Java(TM) SE Runtime Enviroment (build 1.8.0_241-b07)
  • Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)

java.lang.Thread.join を使うとGUIが再描画されない!?

何か重い処理をするときに「しばらくお待ちください」とダイアログを表示したいと思い、以下のような実装を行いました。

showWaitDialog();             // 「しばらくお待ちください」ダイアログを表示
Thread t = new Thread(()-> {  // スレッド作成
  Thread.sleep(10000);        // 重い処理の代わり
});
t.start();                    // スレッド開始
t.join();                     // スレッド終了まで待機
closeWaitDialog();            // ダイアログを閉じる

実行してみると「showWaitDialog()」でダイアログが描画されなかった。

原因

「java.lang.Thread」クラスの「join」関数は、ブロッキング呼び出しで、バックグラウンドスレッドが完了するまでFXアプリケーションスレッドをブロックします。
その結果、UI再描画やユーザー入力に応答しなくなり、今回表示したかったダイアログも表示されませんでした。

解決案:Taskを使用する

今回やりたかったことを実現するには、「Task」を使用することで簡単に実現できました。

showWaitDialog(); // 「しばらくお待ちください」ダイアログを表示
Task<Void> task = new Task<Void>() {
  @Override
  public void call() throws Exception {
    Thread.sleep(10000);  // 重い処理の代わり
    return null;
  }
};

task.setOnSucceeded(e -> {
  // Task正常終了時イベント
  closeWaitDialog();  // ダイアログを閉じる
});

new Thread(task).start();


参考:
http://www.366service.com/jp/qa/d3f162116d8b3dc0c167c9562caff273
https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join(long)

コメント

タイトルとURLをコピーしました