Anurag Anurag - 4 months ago 16
Java Question

Blocked using wait() and notify() in Java

I am writing producer and consumer code using

wait()
and
notify()
in Java.
Thread-0 is created and is invoked on
produce()
and Thread-1 is created and is invoked on
consume()
.

public class Processor {

private volatile List<Integer> list = new ArrayList<>();
private final int MAX_CAPACITY = 5;
Object lock = new Object();

public void produce() throws InterruptedException {

while (true) {

while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");
synchronized (lock) {
lock.wait();
}
}

synchronized (lock) {
int random = new Random().nextInt(100);
list.add(random);
System.out.println("Added to list:" + random);
lock.notify();
}
}
}

public void consume() throws InterruptedException {

while (true) {

while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");
synchronized (lock) {
lock.wait();
}
}

synchronized (lock) {
int i = list.remove(0);
System.out.println("Removed from list:" + i);
lock.notify();
}
}
}
}


The problem is that during execution, program stops after
produce()
:

List is empty!! Consumer is Waiting...
Added to list:22
Added to list:45
Added to list:72
Added to list:91
Added to list:51
List is full! Producer is Waiting....


I am not able to understand what's the problem here. I somehow figured out that wrapping the code from
while
loop in
synchronized
block in
produce()
and
consume()
solves the problem.

produce()


synchronized (lock) {
while (list.size() == MAX_CAPACITY) {
System.out.println("List is full! Producer is Waiting....");

lock.wait();
}


consume


synchronized (lock) {
while (list.size() == 0) {
System.out.println("List is empty!! Consumer is Waiting...");

lock.wait();
}
}


What is the issue here? Is it a case of Thread starvation or deadlock?

Edit: Calling class:

public class App {
public static void main(String[] args) {
final Processor processor = new Processor();

Runnable r1 = new Runnable() {

@Override
public void run() {
try {
processor.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}

}
};

Runnable r2 = new Runnable() {

@Override
public void run() {
try {
processor.consume();
} catch (InterruptedException e) {

e.printStackTrace();
}
}
};

Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);

t1.start();
t2.start();


}
}

Answer

When you perform list.size() it is not thread safe and there is no guarentee you will ever see the value changed in another thread. The JIT could even inline the value if it detects you are not changing it in that thread.

By placing the synchronized block outside the loop you ensure a change in the value is visible (as it is also inside the while(true) loop.

Comments