I have to solve producent-consumer problem using BlockingQueue. I want it to be 1 element queue. So solution have to be:
Produce 1 Consume 1 Produce 2 Consume 2 Produce 3 Consume 3
But it is:
Produce 1 Produce 2 Consume 1 Consume 2 Produce 3 Produce 4 etc.
I am currently using SynchronizedQueue like this:
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();
}
}
What am I doing wrong? I also tried ArrayBlockingQueue and LinkedBlockingQueue.
Context switches can happen anywhere, here they can keep the log messages from being written in a timely way. Say your consumer takes something out of the queue, that notifies the other thread and it can put something in it, all before the consumer can execute the println. The context switch falls after the queue take and before the println. It doesn’t matter if these method calls are part of the same statement or not, the queue gives up its lock when its method call finishes.
But you say, “yes but the other thread causes a notify too shouldn’t that cause another context switch?” Context switches aren’t forced, they’re up to the OS, and it can stick with a thread or not. It may decide it wants to minimize the number of switches.
For the producer the println comes first. But you don’t need a notify to context switch, it can happen whenever.
You can try having both threads synchronize on the shared queue within the loop:
for (int i = 0; i < 100; i++) {
synchronized (blockingQueue) {
consume(blockingQueue.take());
}
}
(I’m showing only one but both threads need to be changed to acquire the same lock.)
This will make sure the println reflects the most recent thing that happened to the queue.