5

私はマルチスレッドに精通していません。1つのプロデューサースレッドでスクリーンショットを繰り返し撮ろうとしています。これにより、BufferedImageオブジェクトが追加されConcurrentLinkedQueue、コンシューマースレッドがオブジェクトをpollキューにBufferedImage入れてファイルに保存します。notify()繰り返しポーリング(whileループ)することでそれらを消費することはできますが、とを使用してそれらを消費する方法がわかりませんwait()。小さなプログラムで使用wait()してみnotifyましたが、ここでは実装できませんでした。

私は次のコードを持っています:

class StartPeriodicTask implements Runnable {
    public synchronized void run() {
        Robot robot = null;
        try {
            robot = new Robot();
        } catch (AWTException e1) {
            e1.printStackTrace();
        }
        Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit()
                .getScreenSize());
        BufferedImage image = robot.createScreenCapture(screenRect);
        if(null!=queue.peek()){
            try {
                System.out.println("Empty queue, so waiting....");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            queue.add(image);
            notify();
        }
    }
}

public class ImageConsumer implements Runnable {
        @Override
        public synchronized void run() {
            while (true) {
                BufferedImage bufferedImage = null;
                if(null==queue.peek()){
                    try {
                        //Empty queue, so waiting....
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    bufferedImage = queue.poll();
                    notify();
                }
                File imageFile = getFile();
                if (!imageFile.getParentFile().exists()) {
                    imageFile.getParentFile().mkdirs();
                }
                    try {
                        ImageIO.write(bufferedImage, extension, imageFile);
                        //Image saved
                    catch (IOException e) {
                        tracer.severe("IOException occurred. Image is not saved to file!");
                    }
                }
            }

以前は、BufferedImageオブジェクトの存在を確認するためにポーリングを繰り返していました。今、私はrunメソッドを変更synchronisedし、実装しようとしましwait()notify()。私は正しいことをしていますか?助けてください。ありがとう。

4

3 に答える 3

5

最初の問題は、プロデューサーでの不必要な待機です。

    if(null!=queue.peek()){ // You are the producer, you don't care if the queue is empty
        try {
            System.out.println("Empty queue, so waiting....");
            wait(); // This puts you to bed, your waiting and so is your consumer
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }else{
        queue.add(image);
        notify();
    }

必要なのはこれだけです。

        queue.add(image);
        notify();

次の問題はnotifyあなたの消費者に不必要です。それはその時点での処理の制御をもたらします。これは、プロデューサーを動かす方法として意図したものだと思いますが、もちろん、コードがその時点に到達することはありません。したがって、この:

            }else{
                bufferedImage = queue.poll();
                notify();
            }
            File imageFile = getFile();
            if (!imageFile.getParentFile().exists()) {
                imageFile.getParentFile().mkdirs();
            }
                try {
                    ImageIO.write(bufferedImage, extension, imageFile);
                    //Image saved
                catch (IOException e) {
                    tracer.severe("IOException occurred. Image is not saved to file!");
                }
            }

次のようになります。

            }else{
                bufferedImage = queue.poll();

                File imageFile = getFile();
                if (!imageFile.getParentFile().exists()) {
                   imageFile.getParentFile().mkdirs();
                }

                try {
                    ImageIO.write(bufferedImage, extension, imageFile);
                    //Image saved
                catch (IOException e) {
                    tracer.severe("IOException occurred. Image is not saved to file!");
                }
            }
于 2012-01-11T20:10:24.043 に答える
5

Queueあなたはその仕事に間違ったものを使っています。はConcurrentLinkedQueue非ブロッキングキューです。これは、プロデューサーとコンシューマーのセマンティクスがないことを意味します。リーダーとライターを1つずつ実行している場合は、SynchronousQueueを確認してください。

簡単に言えば、コードはそのように書き直すことができます

BlockingQueue<?> queue = new SynchrnousQueue<?>();
class StartPeriodicTask implements Runnable {
    public void run() {
        Robot robot = null;
        try {
            robot = new Robot();
        } catch (AWTException e1) {
            e1.printStackTrace();
        }
        Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit()
                .getScreenSize());
        BufferedImage image = robot.createScreenCapture(screenRect);
        queue.offer(image); //1
}
public class ImageConsumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                BufferedImage bufferedImage = queue.poll(); //2

                File imageFile = getFile();
                if (!imageFile.getParentFile().exists()) {
                    imageFile.getParentFile().mkdirs();
                }
                    try {
                        ImageIO.write(bufferedImage, extension, imageFile);
                        //Image saved
                    catch (IOException e) {
                        tracer.severe("IOException occurred. Image is not saved to file!");
                    }
            }

それだけです。

説明させてください。// 1行目で、生成スレッドは画像をキューに「配置」します。SynchrnousQueueには深さがないため、場所を引用します。実際に起こることは、スレッドがキューに「このキューから要素を要求するスレッドがある場合は、そのスレッドを与えて続行させます。そうでない場合は、別のスレッドの準備ができるまで待ちます」と伝えます。

行//2は1に似ており、消費スレッドはスレッドが提供されるまで待機します。これは、シングルリーダーシングルライターでうまく機能します

于 2012-01-11T20:12:21.097 に答える
4

ライブラリがJDK1.5java.util.concurrentに導入されると、独自の待機/通知ロジックを作成する必要がなくなりました。2012年に、独自の待機/通知を行っている場合は、作業が多すぎるため、試行錯誤された真のjava.util.concurrentの同等物を強く検討する必要があります。

そうは言っても、ポーリングは組み込みの背後にある考え方だと思いますjava.util.concurrent.ConcurrentLinkedQueue。言い換えると、コンシューマーは、ConcurrentLinkedQueからの独自のThreadおよび.poll()アイテムに、それがである限り座っています!isEmpty()。私が見たほとんどの実装は、のテストの間にある種の1秒間のスリープをスローしますが、!isEmpty()それは実際には必要ではないと思います。また、私の答えに対するVintの人のコメントに注意してください、.poll()戻るかもしれませんnulljava.util.AbstractQueue探しているものに近いブロッキング動作を持つ可能性のある代替実装を検討してください。

この男は簡単な例を持っています:http: //www.informit.com/articles/article.aspx?p = 1339471&seqNum = 4

最後に、Goetzの本「JavaConcurrencyInPractice」を入手して読んでください。私はそれがあなた自身の自家製の待機/通知を置き換えるために何を使うべきかについてのレシピを持っているとほぼ確信しています。

于 2012-01-11T20:09:44.103 に答える