ここでは、スレッドの動きを少し詳しくみていきます。
前回のサンプルプログラムにおいて、現実世界ではありえない事象が発生していました。それは、一時的?にマイナスのガソリン量を保持してしまうという事象です。
今回は、その事象を解消するために、、以下の2点を仕様に盛り込みたいと思います。
以下の仕様、サンプルコードで具体例を示します。
※なお、赤字の箇所については、「4. スレッドの同期について(2/2)」からの追加・変更点を表しています。
<<サンプルコードの仕様説明>>> ※仕様追加・変更箇所は赤字で記述 ・RacingCarクラス(スレッド処理) 車自身です。 共有のガソリンスタンド(GasStandクラス)から1周毎に1Lのガソリンを補給します。 例では、サーキット5周を走行します。 ・CarRaceクラス 車(RacingCarクラス)の初期設定、及びスタートを行います。 ガソリン補給(Replenishmentクラス)の初期設定、及び補給開始を行います。 ガソリンスタンド(GasStandクラス)の初期設定(ガソリンの初期量)、残量の表示を行います。 ※最後の残量表示は、全スレッドの終了(joinメソッド)を待って表示します。 例では、車を3体用意し、各々が別スレッドで処理を行い、順位を競わせます。 ・GasStandクラス ガソリンを、1L単位で車に供給します。 (ただし、ガソリン残量が0Lの場合は待機します。) ガソリンを補給してもらいます。 (ただし、ガソリン残量が1L以上の場合は待機します。) ・Replenishmentクラス(スレッド処理)※新規追加 ガソリンスタンド(GasStandクラス)にガソリン補給を行います。 CarRaceクラスの仕様より15回(1回に1L)補給します。
■サンプルコード
5-1-1. RacingCar.java
「4-1-1. RacingCar.java」と同じ
5-1-2. CarRace.java
「4-1-2. CarRace.java」と同じ
5-1-3. GasStand.java ※赤字の箇所は、「4-2-3.GasStand.java」からの追加・修正点
class GasStand{ private int totalGas; public GasStand(int gas){ this.totalGas = gas; } public synchronized void getGas(){ int tmpGas = this.totalGas; //ガソリン総量が0Lの間はループ&wait待機(0L以外の場合、車補給処理) while(tmpGas == 0){ try { wait(); }catch(InterruptedException e){ e.printStackTrace(); } //最新のガソリン総量を取得 tmpGas = this.totalGas; } try{ //補給時間0.5秒スリープ Thread.sleep(500); }catch(InterruptedException e){ e.printStackTrace(); } this.totalGas = tmpGas - 1; //全待機スレッドを実行可能状態へ notifyAll(); } public synchronized void putGas(){ int tmpGas = this.totalGas; //ガソリン総量が0L以外の間はループ&wait待機(0Lの場合、スタンド補給処理) while(tmpGas != 0){ try { wait(); }catch(InterruptedException e){ e.printStackTrace(); } //最新のガソリン総量を取得 tmpGas = this.totalGas; } try{ //補給時間0.25秒スリープ Thread.sleep(250); }catch(InterruptedException e){ e.printStackTrace(); } this.totalGas = tmpGas + 1; //全待機スレッドを実行可能状態へ notifyAll(); } public void printTotalGas(){ System.out.println("\n■ガソリンスタンド■ ガス総量"+this.totalGas+"L\n"); } }
5-1-4. Replenishment.java
「4-1-4. Replenishment.java」と同じ
■処理イメージ
今回のサンプルプログラムで行っている処理のイメージを下図で示します。
またwaitメソッド、notifyAllメソッドをという新しいメソッドをについて先に説明しておきたいと思います。
■実行結果イメージ
■ガソリンスタンド■ ガス総量0L ***** 補給 第1回目1L 2号車 1周目通過 ***** 補給 第2回目1L 3号車 1周目通過 ***** 補給 第3回目1L 1号車 1周目通過 ***** 補給 第4回目1L 3号車 2周目通過 ***** 補給 第5回目1L 2号車 2周目通過 ***** 補給 第6回目1L 1号車 2周目通過 ***** 補給 第7回目1L 2号車 3周目通過 ***** 補給 第8回目1L 1号車 3周目通過 ***** 補給 第9回目1L 3号車 3周目通過 ***** 補給 第10回目1L 1号車 4周目通過 ***** 補給 第11回目1L 2号車 4周目通過 ***** 補給 第12回目1L 1号車 5周目通過 1号車 GOAL!! ***** 補給 第13回目1L 3号車 4周目通過 ***** 補給 第14回目1L 2号車 5周目通過 2号車 GOAL!! ***** 補給 第15回目1L 3号車 5周目通過 3号車 GOAL!! ■ガソリンスタンド■ ガス総量0L
ガソリン補給スレッドが1L補給を行う毎に、車スレッドが1L消費し処理順の制御がうまく行われています。
今回のサンプルプログラムだと、時点時点でのガソリン残量がスイッチになり、残量0LならputGasメソッドのスイッチがONになり1L補給、はたまたそれ(残量1L)がgetGasメソッドのスイッチになり1L消費され、という風に処理が切り替え切り替えで処理順の制御を行っています。