0

私は次のコードを持っています。2つのスレッドがスタックに要素があるかどうかをチェックし、ある場合はそれをポップします。何らかの理由で、2つのスレッドの1つは常にjava.util.EmptyStackExceptionを取得します。これは、空のスタックをポップすることを意味します。私の質問は、このメソッドを同期させることでこれを防ぐべきではないかということです。また、同期されたメソッドを使用するだけでエラーを防ぐことは可能ですか、それとも何らかの結合/カウントダウンラッチ/サイクリックバリエなどを実装する必要がありますか?私が尋ねている理由は、理想的には、常に1つのスレッドだけがスタックプレートにアクセスできるようにしたいからです。

public synchronized void checkPlate(){
        //System.out.println(this.name + " checks the plate for pancake");
        Boolean isEmpty = Producer.plate.isEmpty();
        if ( isEmpty == false){
            System.out.println(this.name + " grabs a pancake");
            Producer.plate.pop();
            eat();
            this.eatenPancakes +=1;
        }else{

            try {
                Thread.sleep(200);// waits one minute seconds instead of minutes
                checkPlate();
            } catch(InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }

更新
提案の後、このコードを使用してプロデューサープレートに同期ブロックを実装してみることにしました。そうすると、すべてのスレッドがフリーズしたように見えます。この同期されたブロックをプロデューサーオブジェクト/スレッドにも追加する必要がありますか?

 public void checkPlate(){
        //System.out.println(this.name + " checks the plate for pancake");
        synchronized (Producer.plate){
            Boolean isEmpty = Producer.plate.isEmpty();
            if ( isEmpty == false){
                System.out.println(this.name + " grabs a pancake");
                Producer.plate.pop();
                eat();
                this.eatenPancakes +=1;
            }else{

                try {
                    Thread.sleep(200);// waits since theres no pancake
                    checkPlate();
                } catch(InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    } 


より多くの情報を提供するために2つの目標を更新します。各スレッドがパンケーキをつかんだ後、次の方法でランダムな時間食べたり寝たりします。理想的には、プログラム全体の目標は、プロデューサーが絶えずパンケーキを調理してスタックに追加する場所をシミュレートすることです。2つのスレッドの1つがスタックをチェックし、スタックに要素がある場合はパンケーキを取得します。これは、プロデューサースレッドがパンケーキの調理を終了するまで発生します。

public void eat(){
        Random ran = new Random();
        double eatingTime;
        if (this.name == "Piggy"){
            eatingTime = ran.nextDouble() * (4 - 2) + 2; //generates a value between 2 and 4
        }else{
            eatingTime = ran.nextDouble() * (5 - 3) + 3; //generates a
        }
        try {
            System.out.println(this.name + " starts eating a pancake...");
            Thread.sleep((long)eatingTime *100);//mili seconds instead of minutes
            Boolean isEmpty = Producer.plate.isEmpty();
            if (Producer.finished == false && isEmpty == false){
                checkPlate();
            }
        } catch(InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
4

2 に答える 2

4

私の質問は、このメソッドを同期させてこれを防ぐべきではないということですか?

基本的に、あなたは私たちに言うのに十分な情報を提供していません。ただし、このメソッドを含むクラスのインスタンスが 2 つある場合は、絶対にありません。それらは異なるモニターを持つため、両方が同時に同期メソッド内にある可能性があります。

静的変数 ( ) を共有キューとして使用しているようです。この場合、が呼び出されるインスタンスが複数存在する可能性Producer.plateがあるため、インスタンス レベルの同期は基本的に役に立ちません。checkPlate

とにかく、同期はおそらく不要です。基本的に、のコレクションの 1 つを使用する必要があります。java.util.concurrentこれらのコレクションは同時アクセス用に設計されており、プロデューサー/コンシューマー キューに最適です。

さらに、現在のコードは現在、プレートが空である間、ますます深く再帰します。これは良い考えではありません。アイテムを消費するまで実行し続けたい場合はwhile、再帰の代わりにループを使用する必要があります。

于 2013-02-26T07:26:12.297 に答える
1

残念ながら、呼び出し部分は表示されませんでした。最も重要なことは、このメソッドが同じオブジェクトで呼び出されているか、別のオブジェクトで呼び出されているかを確認する必要があることです。

同じリソース (プロデューサー) を共有する 2 つの異なるオブジェクトからメソッドを呼び出すと、問題が発生します。

それが起こっているようです。

また、別の問題があると思います。スタックが十分に大きい場合、再帰によりスタック オーバーフロー例外が発生します。


明確化後の更新

基本的に、同期化されたメソッドは、モニターがオブジェクト自体である同期化されたブロックと同じです。

synchronized(this) {
    // code
}

2 つの別個のオブジェクトがあるため、それらの 'this' は同じオブジェクトではなく、実際にはリソースをロックしません。

リソースをロックするための単純で古典的なガイドラインは、共有リソースによってアクセスを同期する必要があるというものです。Producer.plate を共有するので、次のことができます。

synchronized(Producer.plate) {
    // code
}

他のオブジェクトもモニターとして使用できますが、Product.plate への同期アクセスを保証するためにそれらが適切に管理されていることを確認するのは開発者の責任です。


UPDATE は、プロデューサが再帰なしでクッキングを終了するかどうかをチェックする redone メソッドを提供します。

public void checkPlate(){
    while(!Producer.finished)
    //System.out.println(this.name + " checks the plate for pancake");
    synchronized (Producer.plate){
        Boolean isEmpty = Producer.plate.isEmpty();
        if ( isEmpty == false){
        System.out.println(this.name + " grabs a pancake");
        Producer.plate.pop();
        eat();
        this.eatenPancakes +=1;
        }
    }
    try {
        Thread.sleep(200);// waits since theres no pancake
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}
} 
于 2013-02-26T07:27:01.150 に答える