I am trying to understand the producer consumer problem. To replicate the issue I wrote the following code
package com.company;
import java.util.ArrayDeque;
import java.util.Queue;
public class ProducerConsumerProblem {
private static Queue<Integer> q = new ArrayDeque<>(5);
public static void main(String args[]) {
Producer p = new Producer();
Consumer c = new Consumer();
Thread t = new Thread( p );
Thread t1 = new Thread( c );
t.start();
t1.start();
}
public static class Producer implements Runnable {
@Override
public void run() {
while(true) {
if (q.size() == 5) {
continue;
}
System.out.println( "Adding to queue" );
q.add( 1 );
}
}
}
public static class Consumer implements Runnable {
@Override
public void run() {
while(true) {
if (q.size() == 0) {
continue;
}
System.out.println( "Removing from queue" );
q.remove();
}
}
}
}
I was expecting to get something like concurrent exception
but instead the code stops when the queue is empty or the queue is full. My question is why the code stops when queue is empty or full because I have placed both consumer and producer in an infinite loop, even if they conucrrently do bad reads but at some later point they will do good read and either producer or consumer should work. Please help me with the situation
It's possible for this two threads to see different sizes as your code isn't thread safe and your add & remove operations are done on different threads.
When you do q.add( 1 );
on thread 1, it doesn't necessarily write the updated array on the memory (i.e. RAM), it may still be in CPU registers and when a context switch occurs for this two threads, your change also may be gone for the second thread, and it doesn't see the updated size. If you use synchronization, all changes in a synchronization block are persisted into the RAM, when the sychnronization block ends, so that unwanted behavior doesn't occur anymore.
Since ArrayDeque
isn't thread safe, you'd need external synchronization to make your code thread safe, or use concurrent data structures.