NetBeansのプロファイラを使ってスレッド状態を監視するサンプルを紹介します。
NetBeansプロファイラのインストール
NetBeans プロファイラは、NetBeans5.5に加えて別途インストールする必要があります。
こちらからダウンロードできます。
ちなみにJDK1.4はプロファイラに対応していないのですが、Profiler用のJDK1.4.2が配布されているのでこれを使うとJDK1.4でもプロファイリングすることができます。
このページの「Modified 1.4.2 JVM」をダウンロードしてNetBeansから使用する。
これでNetBeans プロファイラを使用する準備が整います。
長い処理で画面描画が停止してしまう
「新規ファイル…」から、「Java GUIフォーム」カテゴリの、「JFrameフォーム」を作成します。
クラス名はEDTTest、パッケージはthreadとしました。
JFrameフォームをデザインします。JProgressBarとJButtonを置いて、こんな感じの画面デザインにします。
ボタンのactionPerfomedイベントを記述します。
プログレスバーを0~100%まで徐々に増加させるように処理を記述します。
以下はEventDispatchスレッドで処理待ちをする悪い例です。
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jButton1.setEnabled(false);
//ダメダメなプログレスバーの処理
jProgressBar1.setValue(0);
while(jProgressBar1.getValue() < jProgressBar1.getMaximum()){
jProgressBar1.setValue(jProgressBar1.getValue() + 1);
try {
Thread.sleep(20);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
jButton1.setEnabled(true);
}
実行し「一定時間待つ」ボタンを押してみます。プログレスバーが増加中はJFrameが描画されません。
一定時間待つと100%に表示されてしまいます。
↓(途中の進捗状態が描画されない)
また、待っている間に他のウィンドウで隠してからどかしてみると、隠された部分が再描画されません。
プロファイリング
待っている間に進捗状況が表示されないと、プログレスバーの意味がありません。
この原因を調べるために、プロファイラを使用してみます。
メニューから[プロファイル]-[主プロジェクトをプロファイル]を選択します。
プロジェクトで始めて実行する場合には、質問ダイアログが表示されますが、「了解」ボタンを押します。
「プロファイルタスクの選択」ダイアログでは、実施するプロファイリングタスクを選択します。
ここでは、「アプリケーションを監視」を選択し、「実行」ボタンを押します。
アプリケーションが実行されると、スレッドの状態を表すグラフが表示されます。
この状態で、アプリケーションの「一定時間待つ」を押すと、スレッドの表示が変化します。処理待ちの間は、AWT-EventQueue-0というスレッドが休眠中(紫色)の表示になるはずです。
AWT-EventQueueとはイベントディスパッチスレッドというスレッドで、Swingアプリケーションの描画処理を担当するスレッドです。このスレッドは大部分が待機状態にでないと、描画が乱れたりする不具合に繋がります。このスレッドで長時間かかる処理を記述してしまうと、長時間の処理中は、描画が行われなくなってしまいます。
現在のプログラムは、イベントディスパッチスレッドで処理待ちをしているために、画面描画も停止してしまうわけです。
別スレッドで処理を実行する
ボタンをもう1つ配置して、別の方法で処理を記述してみます。
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
jProgressBar1.setValue(0);
jButton2.setEnabled(false);
//別スレッドで時間のかかる処理を実施する
Thread th = new Thread(){
public void run(){
try {
while(jProgressBar1.getValue() < jProgressBar1.getMaximum()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
jProgressBar1.setValue(jProgressBar1.getValue() + 1);
}
});
Thread.sleep(20);
}
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
jButton2.setEnabled(true);
}
});
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (InvocationTargetException ex) {
ex.printStackTrace();
}
}
};
th.start();
}
このように別スレッドでプログレスバーを徐々に増加させる処理を記述すれば、意図したとおりに描画されます。
プロファイル結果を見ると、イベントディスパッチスレッドはほとんど待機状態になっていることが分かります。
ちなみにSwingで別スレッドで処理させる場合には、SwingWorkerというクラスを用意するのが常套手段です。
http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
SwingWorkerはJDK6.0では標準クラスになっています。