如何使用 BlockingQueue 解决容量为 1 的生产者消费者问题?

How to solve producent consumer problem with capacity 1 using BlockingQueue?

我必须使用 BlockingQueue 解决生产者-消费者问题。我希望它是 1 个元素队列。所以解决方案必须是:

生产1 消耗 1 制作2 消耗 2 制作3 消耗3

但它是:

生产1 制作2 消耗 1 消耗 2 制作3 生产 4 等等

我目前是这样使用 SynchronizedQueue 的:

class Producent extends Thread {
private final BlockingQueue<Integer> blockingQueue;

public Producent(BlockingQueue<Integer> blockingQueue) {
    this.blockingQueue = blockingQueue;
}

@Override
public void run() {
    for (int i = 0; i < 100; ++i) {
        try {
            System.out.println("PRODUCE: " + i);
            blockingQueue.put(i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

class Konsument extends Thread {
private final BlockingQueue<Integer> blockingQueue;

public Konsument(BlockingQueue<Integer> blockingQueue) {
    this.blockingQueue = blockingQueue;
}

public void run() {
    for (int i = 0; i < 100; ++i) {
        try {
            consume(blockingQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

    private void consume(int number) {
        System.out.println("CONSUME: " + number);
    }
}

public class PKmon {
    public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue = new SynchronousQueue<>();
        Thread producent = new Producent(blockingQueue);
        Thread konsument = new Konsument(blockingQueue);
        producent.start();
        konsument.start();
    }
}

我做错了什么?我还尝试了 ArrayBlockingQueue 和 LinkedBlockingQueue。

上下文切换可以发生在任何地方,在这里它们可以防止日志消息被及时写入。假设你的消费者从队列中取出一些东西,通知另一个线程并且它可以在其中放入一些东西,所有这些都在消费者可以执行 println 之前。上下文切换发生在队列 take 之后和 println 之前。这些方法调用是否属于同一语句并不重要,队列在其方法调用完成时放弃其锁定。

但是你说,“是的,但是另一个线程也引起了一个通知,这不应该引起另一个上下文切换吗?”上下文切换不是强制的,它们取决于 OS,并且它可以坚持或不坚持一个线程。它可能会决定要尽量减少开关的数量。

对于生产者来说,println 是第一位的。但是你不需要通知上下文切换,它可以随时发生。

您可以尝试让两个线程在循环内的共享队列上同步:

for (int i = 0; i < 100; i++) {
    synchronized (blockingQueue) {
        consume(blockingQueue.take());
    }
}

(我只展示了一个,但两个线程都需要更改为获取相同的锁。)

这将确保 println 反映队列中最近发生的事情。