0

プロデューサー/コンシューマー モデルで SynchronousQueue を使用するテスト例を作成しました。しかし、うまくいきません。以下は私のコードです:

public class QueueTest {

    String input;
    int pos;
    BlockingQueue<String> queue;
    volatile boolean exitFlag;

    QueueTest()
    {
        for(int i=0; i<10000; i++)
            input += "abcde";
        input += "X";
        pos = 0;
        queue = new SynchronousQueue<String>();
        exitFlag = false;
    }

    public static void main(String[] args) {
        QueueTest qtest = new QueueTest();
        qtest.runTest();
    }

    void runTest()
    {
        Thread producer = new Thread( new Producer());
        Thread consumer = new Thread( new Consumer());
        producer.start();
        consumer.start();
        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class Producer implements Runnable
    {
        public void run()
        {
            while(true)
            {
                String s = read();
                if(s.equals("X"))
                    break;
                try {
                    queue.put(s);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            exitFlag = true;
        }
    }

    class Consumer implements Runnable
    {
        public void run()
        {
            while(exitFlag == false)
            {
                String s = null;
                try {
                    s = queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                process(s);  
            }
        }
    }

    String read()
    {
        String str = input.substring(pos, pos+1);
        pos++;
        return str;
    }

    void process(String s)
    {
        long sum = 0;
        for(long i=0; i<1000; i++)
            sum = sum * i + i;
    }
}

問題は、実行がデッドロックのようにスタックしていることです。これらの単純なコードにバグはありますか?

4

3 に答える 3

1

その場合、競合状態が発生する可能性が高くなります。シナリオを想像してみてください

Thread 1 put into queue
Thread 2 takes out of queue quickly processes and awaits another put from thread 1
Thread 1 finishes and sets exitFlag to true

この場合、スレッド2がスレッド2から読み取る前にexitFlagがfalseに設定されていなかったため、スレッド2は永続的に配置されます。

あなたは毒薬を検討したいかもしれません。これは、完了した他のスレッドへのメッセージです。例えば:

   final String POISON_PILL = " POISON";

class Producer implements Runnable {
    public void run() {
        while (true) {
            String s = read();
            if (s.equals("X"))
                break;
            try {
                queue.put(s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            queue.put(POISON_PILL);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    public void run() {
        String s = null;
        try {
            while ((s = queue.take()) != POISON_PILL) {
                process(s);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

したがって、他のスレッドに通知されると、他のスレッドが完了したときに、両方のスレッドが正常に終了する必要があります。

于 2012-08-14T19:22:17.557 に答える
0

exitFlag は複数のスレッド間で共有されるため、Producer による更新を Consumer が認識できるようにするために何かを行う必要があります (Java メモリ モデルに関して)。この例では、値を volatile にするだけで十分です。

アップデート:

ハングしたコードのスタック ダンプを生成する必要があります。それは何が起こっているかについての手がかりを与えるでしょう。このコードは、制御にフラグを BlockingQueue と共に使用してはならない理由を示す良い例です。

更新 2:

残念なことに、@JohnVint は猫を袋から出しました。はい、ポイズン ピルはこの競合状態の解決策です。

于 2012-08-14T19:06:23.527 に答える
0

プログラムは、次のシナリオでスタックします。

Producerは、 Consumer がexistFlagtrueかどうかを確認した直後に、(新しい要素を配置せずに) exitFlagを設定します。キューにそれ以上要素がない場合 (コンシューマが以前にすべての要素を処理できた場合)、コンシューマはqueue.take()でブロックされます。

ブロッキングメソッドではないqueue.poll()を使用できます。プログラムを少し変更する必要があります。

于 2012-08-14T19:33:22.513 に答える