EeBlog(テクニカルブログ)

第3回 ある条件が満たされるまで処理を待たせる

皆さんはポーリングループをご存知でしょうか?
ある条件が満たされるまで処理を待たせるため、 ループを繰り返し、処理を進めない手法です。
条件が満たされるまで処理を待ちたいという事は実際の現場では よくあることだと思います
しかし、ポーリングループはパフォーマンスを低下させてしまいます。
そこで、ポーリングループを使用せず、条件が満たされるまで待ちたい場合 マルチスレッドで使用するwaitメソッドと notifyAllメソッドの待ち合わせを 利用します。

以下のサンプルは「肉を焼く」スレッドと 「肉を食べる」スレッドが存在します。 「肉を焼く」スレッドでは肉を使用する権利を得る (synchronizedロックする)前に 1秒間空けているため、 先に「肉を食べる」スレッドが権利を得て動き出します。
しかし、肉の状態が「焼き」に変わるまでwaitメソッドで待機します。 その際、whileループにて条件判定を行っていますが、 ポーリングループではありません。 処理は、waitメソッドにて待機状態となります。 ifでもよいのですが何らかの手違いにより waitメソッドによる待機状態が解除されるのを防ぐために whileループを使用しています。

// バーベキュークラス
 public class Bbq {

     private Meat meat = new Meat();

     public static void main(String[] args) throws Exception {
         Bbq party = new Bbq();
         party.start();
     }

     public void start() {
         new Thread(new Runnable(){
             public void run() {
                 try {
                     Thread.sleep(1000);
                     synchronized (Bbq.this.getMeat()) {
                         System.out.println("肉を焼き始めました。");
                         Thread.sleep(5000);
                         // 肉を焼く(5秒程度)
                         Bbq.this.broil();
                         // 焼き上がりを報告
                         System.out.println("肉が焼けました。");
                         Bbq.this.getMeat().notifyAll();
                     }
                 } catch (Exception e) {
                     throw new Error("肉を焼くのに失敗しました。", e);
                 }
             }
         }).start();

         new Thread(new Runnable(){
             public void run() {
                 try {
                     synchronized (Bbq.this.getMeat()) {
                         // 焼き上がり待ち
                         System.out.println("肉を食べよう!!");
                         while(Bbq.this.getMeat().getCondition() != 
                                             MEAT_CONDITION.GRILLED) {
                             System.out.println("まだ焼けてない(ToT)/");
                             Bbq.this.getMeat().wait();
                         }
                         // 食べる
                         System.out.println("やったーー食べるぞーー!!!");
                         Bbq.this.eat();
                         System.out.println("肉を食べ終わりました");
                     }
                 } catch (Exception e) {
                     throw new Error("肉を食べるのに失敗しました。", e);
                 }
             }
         }).start();
     }

     // 肉焼きメソッド
     public void broil() {
         meat.setCondition(MEAT_CONDITION.GRILLED);
     }

     // 肉食いメソッド
     public void eat() {
         meat = null;
         System.out.println("肉を食べました。");
     }

     // 肉データ取得メソッド
     public Meat getMeat() {
         return meat;
     }

 }

 //肉の状態
 enum MEAT_CONDITION {
     RAW,
     GRILLED
 }

 // 肉データ
 class Meat {

     private MEAT_CONDITION condition = MEAT_CONDITION.RAW;

     public MEAT_CONDITION getCondition() {
         return condition;
     }

     public void setCondition(MEAT_CONDITION condition) {
         this.condition = condition;
     }

 }

肉が焼けるまで5秒待ってね!!