新しいスレッドで実行させる
では、先ほどのサンプルをタイマーではなく、スレッドを利用した形に修正してみましょう。TimeLabelクラスを修正してみることにします。
/* import宣言は省略 */ class TimeLabel extends JLabel { private static final long serialVersionUID = 1L; private DateFormat format; private boolean roop_flag; public TimeLabel(){ this.setFont(new Font("Dialog",Font.BOLD,24)); format = new SimpleDateFormat("HH:mm:ss"); roop_flag = true; Thread thread = new TimerThread(); thread.start(); } public synchronized void setTime(){ Calendar calendar = Calendar.getInstance(Locale.JAPAN); this.setText(format.format(calendar.getTime())); } class TimerThread extends Thread { public void run() { int n = 0; while(roop_flag){ try { Thread.sleep(1000); } catch(InterruptedException e){ e.printStackTrace(); } System.out.println(n++); setTime(); } } } }
こんな感じになりました。ここでは、TimeLabel内にTimerThreadというスレッド実行のためのクラスを用意し、TimeLabelの表示更新を行っています。
スレッド実行の仕組み
このようにスレッドは、目的の処理を実装したオブジェクトを実行する形で処理を行いますが、そのオブジェクトの定義方法は大きく分けて2種類あります。一つは、今あげた例のようにThreadクラスを継承して新しいクラスを定義する方法。もう一つは、Runnableというインターフェイスをimplementsする(これは、この後で説明します)という方法です。これ以外のクラスは、スレッドとして実行することができません。
Threadのサブクラスは、インスタンスを作成し、開始用のメソッドstartを呼び出すことで、そのインスタンスに用意してある処理を、新しいスレッドで実行することができます。ここではTimeLabel内にTimerThreadというクラスを用意しました。コンストラクタ部分を見てください。TimerThreadインスタンスを作成後、startというメソッドが呼び出されていますね。これが、新しいスレッドで処理を開始するためのメソッドです。
Thread thread = new TimerThread();
thread.start();
ところで、TimerThreadにあるどのメソッドでも新スレッドで実行できるというわけではありません。新スレッドで実行する処理はすべてrunメソッドの内部に記載する必要があります。Threadのサブクラスには、このrunメソッドを必ず用意する必要があります。
今回のサンプルコードでは、runメソッドでどのような処理を行っているのでしょうか。タイマーの場合のコードとは違って、単にsetTimeを呼び出すだけではなさそうです。
public void run() { int n = 0; while(roop_flag){ try { Thread.sleep(1000); } catch(InterruptedException e){ e.printStackTrace(); } System.out.println(n++); setTime(); } }
ループとsleepメソッド
まず注目してほしいのはwhile(roop_flag)の部分です。runメソッド内の処理は無限ループの中に書かれています。先ほど説明したタイマーでは1回のタスクが完了した後も、一定間隔で繰り返し呼び出される仕組みとなっていました。
一方、Threadのrunメソッドは1回きりしか呼び出されません。そしてスレッドはrunメソッドの実行が終われば消滅してしまいます。もう一度処理を呼び出すためには、新しいThreadインスタンスを作成し、startしなければならないのです。
そこで、定期的に繰り返して処理を行わせたい場合は、ループを使ってメソッドの終了とスレッドの消滅を回避します。サンプルでは特に想定していませんが、特定の条件でスレッドを終了させたい場合はroop_flagの値をfalseに変更します。ループを抜けるとrunメソッドの処理が完了し、スレッドは終了します。考え方としては非常に単純な仕組みです。
ただし、そのままでは問題があります。ただループ内で処理を実行してしまうと、猛烈な勢いで処理を繰り返し続けることになってしまいます。いくら別スレッドとはいえ、これではCPUのパワーを無駄に消費してしまいます。タイマーのような定期的な処理を行いたい場合は、一定時間の待機を行う必要があります。
サンプルコードで一定時間の待機を行っているのが、try内に記載されたThread.sleepです。Threadクラスのsleepメソッドは、引数に指定したミリ秒数だけ現在のスレッドの処理を一時停止します。今回は、1秒停止してからsetTimeを呼び出すようにしています。なお、このsleepメソッドはInterruptedExceptionという例外を発生させる場合があるので、例外処理を忘れないようにしてください。
