Javaのスレッドは、仮想マシン上で実装されているおかげか、比較的単純な構造になっています。もちろん、構造が単純でもなかなか難しい問題もあるので「簡単」というわけではありませんが、少なくとも「わかりやすい」とは言えそうです。 スレッドにはいろいろ厄介な問題があるわけですが、そういった問題をどうすればわかりやすく解決できるのか、Javaを例に実験してみましょう。今回は、まずスレッドを作って終了させる、という基本動作を確認しておきます。 スレッドを作るJavaでスレッドを作るには、以下の二つの方法があります。
このうち、2の方は問題ないでしょう。ごく初期のころからアニメなどのJavaアプレットでの定型句「extends java.applet.Applet implements Runnable」というあの宣言ですね。Runnableを実装するとrun()メソッドに書いた処理をスレッドで実行できましたが、それは1の方法でも基本的には同様です。 つまり、スレッドで実行したい処理をクラス内のrun() メソッドに書いておき、start()でスレッドを起動。起動されたスレッドは、run()の処理が終了するかstop()で強制終了されるまで処理を続ける、という事です。Threadのサブクラスは以下のような構造になっています。 class SThread extends Thread { // スレッドクラス SThread() { // コンストラクタ 初期化処理 } public void run () { スレッドの処理 } } このスレッドクラスで定義した処理を別スレッドで行うためには、 SThread th; th=new SThread(); // スレッドクラスのインスタンス作成 th.start(); // スレッド起動 のようにします。 スレッドが「生きて」いる間は、thのisAlive()メソッドがtrueを返します。スレッドを終了させる時、確実に終了させたければ(スレッドが終了するのを待って次の処理をしたければ)、isAlive()メソッドで継続判定を行って以下のように書くと良いでしょう。 変数などでth に対してスレッド終了を指示 while (th.isAlive()); // th のスレッドが終わるのを待つ なお、JavaのThreadクラスにはスレッドの終了を待つ関数としてjoin() というものがあり、これを使ってもスレッドの終了を待つ事ができます。join()では、引数に最大の待ち時間を指定する事も出来て(指定しないと終了されるまでいつまでも待つ)便利ですが、どうも一部の環境ではjoin()がうまく行かない場合があるようですね。とりあえず機能の上では、上の処理のwhileループはth.join()としたのと同様の効果を得られる「はず」です。 プログラム今回のプログラムは、「数を数えてそれを指定されたテキストフィールドに表示する」スレッドを二つ作ります。Javaアプレットにはテキストフィールドが二つあり、ここにスレッドが数えている値を表示するわけです。テキストフィールドの数値を見ていれば、数値の増え方でスレッドの実行状況(スケジューリング)を確認できるでしょう。たいていは、二つの数値が同じようなペースで増えていくはずで、これは二つのスレッドが同じように実行の機会を与えられている事を意味します。 ただし、環境(Java仮想マシンの実装)によってはどちらかのスレッドばかりが実行され、どちらかの数値だけが増えていく、という場合もあるかもしれません。数は0から始めて1づつ加算し、それをオブジェクトを作る時に(コンストラクタの引数として)渡されたテキストフィールドに表示します。 class ThreadTest extends Thread { // スレッドクラス int s; volatile public boolean go; TextField f; ThreadTest(TextField tf) { // コンストラクタ f=tf; go=true; s=0; } public void run () { // スレッドの処理 while (go) { // 終了通知を受けるまで処理を続行 f.setText(String.valueOf(s)); // 数を表示 s++; // 数を1増やす } f.setText("Thrad Stop"); // 終了時のメッセージ表示 } } スレッドを起動するには、アプレット内のStart! クリックしてください。ボタンをクリックすると、スレッドクラスのインスタンスを二つ作り、起動します。スレッドを終了させる時は、Stop!ボタンをクリックすると、変数goをfalseにしてスレッドの終了を指示し、終了するまで待ちます。 プログラムソース表示 |