第15章 スレッド (Java)
スレッドの作成方法
- Runnableインタフェースを実装する方法
- Runnableインタフェースを実装したクラスを作成し、runメソッドをオーバライドする。
- Threadクラスのコンストラクタの引数にRunnableのインスタンスを渡す。
- staartメソッドを呼ぶ
実務では、圧倒的にこの方法が使用されている。
- Threadクラスを継承する方法
- Threadクラスを継承したクラスを作成し、runメソッドをオーバーライドする。
- staartメソッドを呼ぶ
この方法は、簡単に記述できるが、Thread以外のクラスを継承する場合は、使用できない。
runメソッドを使用するためだけにThreadクラスを継承することは、継承の使い方として、好ましくない。
継承は、Threadクラス自身を拡張したい時に行うべきである。
複数のスレッドの起動
- スレッドは、異なる処理を並行して実行しているのではなく、きわめて短い時間間隔で実行するスレッドを切り替えているため、同時並行的に実行されているように見える。
- どのスレッドを停止して、どのスレッドを動かすかは、JVMに組み込まれているスケジューラが独自の判断で行うため、プログラムでコントロールすることはできす、切替えのタイミングは、まったく予測できず、実行の度に変化する。
- スレッドは、切り替えながら、最後まで実行することだけが確かなことで、あとは何も保障されていない。
スレッドの状態遷移
- スレッドは、startメソッドを実行すると実行可能プールにおかれ、スケジューラがそこから任意のひとつを実行する。
- 実行途中でも、実行可能プールに戻されたり、実行不可能状態になったりする。
- スレッドには、5つの状態がる。
- 初期状態
- newでThreadオブジェクトが作成された状態
- オブジェクトとしては有効であるが、まだスレッドは開始されていない。
- 実行可能状態(実行可能プール)
- startメソッドを実行するとこの状態になる。
- startメソッドを実行しても実行可能プールに入れられるだけで、すぐに実行されるわけではない。
- 実行中
- スレッドが実行されている状態。
- クラスメソッドThread.CurrentThreadメソッドで取得できるオブジェクトの惨状は、実行中のスレッドを参照する。
- 実行中のスレッドは、他のスレッドと切り替えるスケジューラにより実行可能状態に戻されることがある。
- 最後まで実行した場合は、終了状態に移行する。
- sleepメソッド、waitメソッドの実行、ブロックと呼ばれる状態に遭遇すると実行不可能状態に移行する。
- 実行不可能状態
- 実行中のスレッドが、なんらかの事情で一時的に実行を継続できなくなった場合、実行不可能状態へ遷移する。
- 実行不可能状態が解消されると、事項可能状態へ遷移する。
- 直ちに実行中へは戻らない。
- 終了状態
- runメソッドを完了した状態
- 一度終了したスレッドオブジェクトは、2度とstartメソッドを実行できない。
(実行時例外が発生、ただし、オブジェクトが消えたわけではないので、startメソッド以外のメソッドは、実行できる。)
- 初期状態
sleepメソッド
- 実行中のスレッドを指定した時間だけ休止させる。
- Threadクラスのクラスメソッド。オブジェクトを作成しなくてもクラス名を付加するたけで使用できる。
- 休止時間は、ミリ秒で指定(longの整数)
- チェック例外をなげる(InterruptedException)
joinメソッド
- 特定のスレッドの実行が完了するまで、そのスレッドを実行しない(実行不可能状態にする。)
(例)
Thread t =new Thread();
t.join()
とmainメソッド内に記述した場合、mainメソッドがtスレッドの終了を待つ。 - このスレッドが終了すると実行可能状態にする。
- チェック例外をなげる(InterruptedException)
Synchronizedキーワード
- 連続してひと固まりの処理として実行しなければならないメソッドにはSynchronizedキーワードを付ける
- あるスレッドがSynchronizedメソッドの付いたメソッドの実行を開始すると、それが終了するまで他のスレッドは、アクセスできない。
メソッド実行の排他制御ができる(同期化) - 制御のための仕組みは、オブジェクトが持っている。(ロック)
- ロックは、オブジェクトにひとつしかない。そのため、複数の同期メソッドがある場合、あるスレッドがロックを取得したら、他のスレッドは、そのオブジェクトのどの同期メソッドにも入ることはできない。
- 同期メソッドを実行中のスレッドは、処理を完了するまでロックを保持する。
- 同じオブジェクト内に同期メソッドと非同期メソッドが混在してもよい。
- 非同期メソッドは、ロックとは無関係のため実行可能になったスレッドであれば、どの非同期メソッドでも実行できる。
- sleepメソッドでスリープ状態(実行不可能状態)になったスレッドは、ロックを保持したまま解放ししない。そのため、ロック取得できないで待たされるスレッドも実行不可能状態へ移行される。
waitメソッド
- JVMによる割り込み
- チェック例外をなげる(InterruptedException)
- waitをwhile分で囲むのは協調動作の処理では定石
notifyメソッド
- どれか1つ(指定できない)スレッドを実行可能プールへ移す。
notifyAllメソッド
- すべてのスレッドを実行可能プールへ移す。