2

私はマルチスレッドについていくつかの研究を行っており、プログラムを作成しようとしています。

複数の顧客に並行してサービスを提供することをシミュレートするレストラン プログラムを作成しました。

  • レストランが開き、ウェイター、シェフ、何人かの顧客を作成し、すべての顧客が食事を食べるまで待ちます
  • 顧客が注文し、ブール値の「食べられる」が真になるのを待ってから、レストランに通知します
  • ウェイターはクライアントが注文するのを待ってから、シェフに通知します
  • シェフは、ウェイターが注文について彼に通知するのを待ち、食事を準備し、顧客の「食べた」をtrueに設定します

どういうわけか、私のプログラムはほぼ異なる結果で終了します。

私が行った調査の後、別の方法で終了する 2 つの理由がわかりました。1) メソッドが同期されていない場合 (私のプログラムではそうではありません)。2) スレッドのリソースの割り当て方法に影響を与えることはできませんが、スレッドのシーケンスに小さな違いが生じるためです。

しかし、私のプログラムは、スレッドのシーケンスの小さな違いだけでなく、大きな違いで終了します。

  • 顧客が 1 人の場合、常に正しく終了します。
  • 複数の顧客がいる場合、すべてがうまくいき、レストランが閉まることがあります。しかし、シェフが次の注文を受けるべき瞬間に、ウェイターによる2回目の通知の後にスタックすることがあります。終了せず、スレッドは実行されていますが、シェフは次の注文を処理しません。

誰かが私にヒントを教えてもらえますか?

シェフのコード:

class Chef extends Thread{
    private static int _id=1;
    private int id;
    Order order;

    public Chef(){
        this.id=_id;
        _id++;
        order=null;
        this.start();
    }

    @Override
    public void run() {
        System.out.println("Chef ("+id+") starts to work...");

            synchronized(this){
                while(order==null){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            System.out.println("Chef ("+id+") prepared Order ("+this.order.getId()+")");

            Restaurant.customers.get(this.order.getId()-1).served=true;
            synchronized(Restaurant.customers.get(this.order.getId()-1)){
                Restaurant.customers.get(this.order.getId()-1).notify();
            }
                   order=null;
    }

    public void prepareOrder(Order order){

        this.order=order;
        System.out.println("Chef ("+this.id+") prepares order ("+order.getId()+")");
        synchronized(this){
            this.notify();
        }
    }
}

ウェイターのコード (正常に動作し、常に注文を処理します):

class Waiter extends Thread{

    private static int _id=1;
    private int id;
    Order order;

    public Waiter(){
        this.id=_id;
        _id++;
        order=null;
        this.start();
    }

    @Override
    public void run() {
        System.out.println("Waiter ("+this.id+") starts to work...");

        synchronized(this){
            while(takenOrder==false){
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        order=null;

        Restaurant.chefs.get(0).prepareOrder(order);
    }

    public void takeOrder(Order order){

        this.order=order;
        System.out.println("Waiter ("+this.id+") takes order ("+this.order.getId()+")");
        synchronized(this){
            this.notify();
        }
    }
}

コード全体

4

3 に答える 3

2

答え 問題はこれです

synchronized(this){
...
}

上記のコードは、2 つの理由で正しくありません。

  1. 相互排除はありません。各スレッドには、独自のモニター/ロックがあります。ロックは独自のスレッドで見ることができます。したがって、synchronized(this) は冗長です。
  2. Thread インスタンスで同期しないでください (悪い習慣)。あなたの場合、これは Thread のインスタンスです。 もう1つ、スレッドを拡張しないでください。代わりにRunnableを使用しないようにしてください

の解き方?

class Chef implments Runnable {

  private Object lock;
  Chef(Object lock) {
     this.lock = lock;
  }

  public run() {

      synchronized(lock) {
         // do stuff here
      }
  }

}


class Waiter implments Runnable {

  private Object lock;
  Chef(Object lock) {
     this.lock = lock;
  }

  public run() {

      synchronized(lock) {
         // do stuff here
      }
  }

}


//your main

 public static void main(String []args) {
    Object obj = new Object();
    Thread chef = new Thread(new Chef(obj));
    Thread waiter = new Thread(new Waiter(obj));
    chef.start();
    waiter.start();
 }

上記のアプローチは、2 つのスレッド間の相互排除の非常に基本的な例であることが示唆されています。しかし、それは最善のアプローチではありません。BlockingQueue を使用してみてください。目的に最も適している可能性があります

つまり、mutex obj を共有する代わりに、ArrayBlockingQueue インスタンスを共有します。注文キューが空だったり、顧客キューが満杯だったりした場合に待機するなど、多くのことを処理します。

于 2013-07-15T03:25:40.477 に答える
1

質問は実際には理論的なものではなく、明らかにコードに何か問題があります。

推測では、は 2 回目Chefの通知を待つ前に既存の注文をチェックしていないということWaiterです。

シナリオ:

  1. 顧客がウェイターに注文を出す
  2. ウェイターがシェフに通知
  3. シェフが注文を始めます
  4. 顧客がウェイターに注文を出す
  5. ウェイターがシェフに通知
  6. シェフが最初の注文を終える
  7. シェフはウェイターからの通知を待ちます
  8. デッドロック。
于 2013-07-15T01:59:30.513 に答える