如何使用 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 反映队列中最近发生的事情。
我必须使用 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 反映队列中最近发生的事情。