0

Javaでマルチスレッドの例を試しています。Java Complete reference 7th Edition にマルチスレッド同期の例がありました。この例は正常に動作します。しかし、同じクラスの別のスレッドを作成するために行を少し追加すると、これは機能しません。なぜこれが起こっているのか教えてください。以下に例を示します。以下のコードは、プロデューサーとコンシューマーの古典的な例です。単一のプロデューサーがある場合、2 つのプロデューサーがある場合は正常に動作し、失敗します。15時まで置いて止まるだけです。

class Q {

    int n;
    boolean valueSet = false;

    synchronized int get() {
        while (!valueSet) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        }
        System.out.println("Got: " + n);
        valueSet = false;
        notify();
        return n;
    }

    synchronized void put(int n) {
        while (valueSet) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        }
        this.n = n;
        valueSet = true;
        System.out.println("Put: " + n);
        notify();
    }
}

class Producer implements Runnable {

    Q q;

    Producer(Q q) {
        this.q = q;
        new Thread(this, "Producer").start();
        //new Thread(this, "Producer2").start();
    }

    public void run() {
        int i = 0;
        while (true) {
            q.put(i++);
        }
    }
}

class Consumer implements Runnable {

    Q q;

    Consumer(Q q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    @Override
    public void run() {
        while (true) {
            q.get();
        }
    }
}

public class PCFixed {

    public static void main(String[] args) {
        Q q = new Q();
        Producer P1 = new Producer(q);
        new Consumer(q);
        Producer P2 = new Producer(q);
        System.out.println("Press Control-C to stop.");
    }
}
4

3 に答える 3

1

Q は、一度に 1 つの値のみを受け入れるように記述されています。ブール値のメソッドに変更する必要がありputます。valueset が true の場合は true を返し、その後は通常どおり処理されます。valueset が false の場合は false を返し、何もせずに戻ります。次に、呼び出しているメソッドputは、真の応答が得られるまで再試行を続ける必要があります。このようにして、複数のコンシューマが互いに干渉することなく同じ Q オブジェクトを使用できます。

複数のプロデューサーを使用している場合のより良い解決策は、スレッドセーフなキューであるConcurrentLinkedQueueを使用することです。プロデューサーはoffer整数をキューに入れ、コンシューマーはpoll整数をキューに入れます。複数のプロデューサは互いに干渉することなく同時に整数をoffer処理でき、複数のコンシューマはpoll互いに干渉することなく同時に整数を処理できます。

于 2013-04-12T23:35:02.630 に答える
0

の各出現箇所を に置き換えnotifyますnotifyAll

于 2013-05-15T13:39:51.170 に答える
0

提供する同時実行の例では、単一のbooleanフラグを使用して、シグナルがあるかどうかを確認します。

したがって、これはSemaphore生産者と消費者の取り決めというよりも取り決めです。Thread任意の数のsを扱うのは単純すぎます。

本当にプロデューサー コンシューマーを使用したい場合は、複数のアイテムを保持するキューが必要になります。

static final AtomicBoolean run = new AtomicBoolean(true);

static class Producer implements Runnable {

    final BlockingQueue<String> blockingQueue;

    public Producer(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (run.get()) {
            blockingQueue.add("Value from " + Thread.currentThread().getName());
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                //doesn't matter.
            }
        }
    }
}

static class Consumer implements Runnable {

    final BlockingQueue<String> blockingQueue;

    public Consumer(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (run.get()) {
            final String item;
            try {
                item = blockingQueue.take();
            } catch (InterruptedException ex) {
                return;
            }
            System.out.println(item);
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    final LinkedBlockingQueue<String> lbq = new LinkedBlockingQueue<>();
    final ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.submit(new Consumer(lbq));
    for (int i = 0; i < 10; ++i) {
        executorService.submit(new Producer(lbq));
    }
    Thread.sleep(10000);
    run.set(false);
    executorService.shutdownNow();
}

この簡単な例では、 を使用してLinkedBlockingQueueにイベントをポストし、イベントを読み取ります。

は、 s を独自の名前でキューにProducer入れます(これは 100 ミリ秒ごとに行われます)。はキューから取得し、.StringThreadConsumerString

キューは であるBlockingQueueため、takeキューが空の場合、メソッドはブロックされます。

にアイテムを追加するループを変更することで、Producerとの数を簡単に変更できます。実験して、それがどのように機能するかを見てください。ConsumerExecutorService

このAtomicBooleanフラグにより​​、プログラムは生成されたすべての子プロセスをシャットダウンできます。

于 2013-04-13T00:04:24.907 に答える