synchronizedについて
さてrunメソッドの中で、もうひとつ注目してほしい点があります。スレッド内で実行しているsetTimeメソッドです。ここではsynchronizedというものがつけられていますね。 これは一体、何でしょうか?
これは、多重アクセスを防ぐためのものです。新しいスレッドを作って実行する場合、あるオブジェクトが、複数のスレッドから同時にアクセスされる可能性に注意しなければなりません。
例えば、ファイルの読み書きなどを処理をスレッドで実行させたとしましょう。さて、あるスレッドからファイルを読み込んでいる最中に、別のスレッドからファイルを書き換えてしまったらどうなるでしょうか。とりあえず、とんでもないトラブルを引き起こしてしまいそうですよね。
そこで、この部分を実行している間は、他のスレッドからオブジェクトにアクセスできないようにするために用意されているのが、synchronizedです。これをつけたメソッドの実行中は、他のスレッドからそのオブジェクトへアクセスできなくなります。
まぁ、このサンプルでは単に新スレッドからLabelの表示テキストを変更しているだけですので、synchronizedなど使わなくとも問題が起こることはまずないんですが、synchronizedはこんな具合に使うのだな、というひとつの利用例として見てください。
Runnableによるスレッド処理の実装
ここまでは、Threadクラスの継承によってスレッド処理を実現するパターンについて見てきました。ここからは、スレッドを実装するもう一つのやり方である「Runnableインターフェイスを利用するパターン」について見ていきましょう。まずは、先ほどのサンプルコードを修正します。
/* import宣言は省略 */ class TimeLabel extends JLabel implements Runnable { 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 Thread(this); thread.start(); } public synchronized void setTime(){ Calendar calendar = Calendar.getInstance(Locale.JAPAN); this.setText(format.format(calendar.getTime())); } public void run() { while(roop_flag){ try { Thread.sleep(1000); } catch(InterruptedException e){ e.printStackTrace(); } setTime(); } } }
この例では、Threadのサブクラスはありません。代わりに、TimeLabelクラスの宣言にimplements Runnableが追加されています。このRunnableインターフェイスを組み込むことで、このTimeLabelクラスはThreadのサブクラスと同様、新しいスレッド内で実行できるようになります。
implements Runnableしたクラスでは、Threadのサブクラスと同じくrunメソッドを用意し、スレッドで実行する処理を記述します。このあたりは、Threadを継承するパターンと全く同じです。違うのは、スレッドの開始の仕方です。
Thread thread = new Thread(this); thread.start();
コンストラクタでは、まずnew Threadでスレッドを作成しています。この時、引数にRunnableをimplementsしたクラスのインスタンスを渡します。これはインスタンスを新しいスレッドで実行しますよ、という指定です。生成したThreadのstartメソッドを呼び出すと、新しいスレッドが開始されます。
Runnableをimplementsしたクラスでは、runというメソッドを必ず用意する必要があります。startすると、引数に指定されたインスタンス内のrunメソッドが新しいスレッド内で実行されます。
ここまで2種類のスレッドの使用方法を説明しましたが、どちらを使うべき、という決まりがあるわけではありません。ただし、一般にSwing関係のアプリケーションというのは、何らかのGUI関係のクラスを継承して作成されているものです。従って、Thread継承クラスを作成するよりは、Runnableをimplementsする方法が一般的に用いられます。
実をいえば、Threadというクラスの中身は、「Runnableをimplements済みのクラス」なのです。従って、どちらも中身は全く同じことをしているのですが、不必要にThreadクラスのサブクラスを作るよりは、Runnableをimplementsして実装するほうがよいでしょう。Threadを継承する方法は、runメソッドの他にもいろいろと用意する必要があるなど、特別な場合に用いる、と考えてください。
