Registered User Registered User - 1 month ago 16
Java Question

Monitor program using semaphore does not work as expected in java

I am trying to create a multiple producer multiple consumer problem's solution(i.e a Monitor) in java.

I decided to use Semaphore class and synchronized as follows.

import java.util.concurrent.Semaphore;

public class PC implements Runnable {
Semaphore s;
Object lock;
boolean isProducer;
final int ProcessCount = 4;
final int producerDelay = 1500;
final int consumerDelay = 1000;

public static void main(String[] args) {
new PC();
}

PC() {
this.s = new Semaphore(10);
this.lock = new Object();
this.isProducer = true;
// create few consumers and producers
for (int i = 0; i < ProcessCount; i++) {
new Thread(this).start();
}
}

public void run() { //alternately create producers and consumers
if (isProducer) {
isProducer = false;
producer();
} else {
isProducer = true;
consumer();
}
}

void producer() {
try {
while (true) {
synchronized (this.lock) {
s.release(); // produce an resource
}
System.out.println("produced:\tAvailable now:\t" + s.availablePermits());
Thread.sleep(producerDelay);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

void consumer() {
try {
while (true) {
synchronized (this.lock) {
s.acquire(); // consume resource
}
System.out.println("consumed:\tAvailable now:\t" + s.availablePermits());
Thread.sleep(consumerDelay);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}


But the output is not as expected. When producer delay is less than consumer delay, the no of resources available with semaphore go above 10. On the other hand, when producer delay is greater, the program seemingly goes into a deadlock when resource become 0 (while I expect consumer thread to pause, until resource thread produces something)

Example output:

when consumer delay is lower:

consumed: Available now: 10
consumed: Available now: 10
produced: Available now: 11
produced: Available now: 10
consumed: Available now: 8
consumed: Available now: 8
produced: Available now: 10
produced: Available now: 10
consumed: Available now: 9
consumed: Available now: 8
produced: Available now: 9
consumed: Available now: 8
produced: Available now: 9
consumed: Available now: 8
consumed: Available now: 7
consumed: Available now: 6
produced: Available now: 7
produced: Available now: 8
consumed: Available now: 7
consumed: Available now: 6
produced: Available now: 7
produced: Available now: 8
consumed: Available now: 7
consumed: Available now: 6
consumed: Available now: 5
consumed: Available now: 4
produced: Available now: 5
produced: Available now: 6
consumed: Available now: 5
consumed: Available now: 4
produced: Available now: 5
produced: Available now: 6
consumed: Available now: 5
consumed: Available now: 4
consumed: Available now: 3
consumed: Available now: 2
produced: Available now: 3
produced: Available now: 4
consumed: Available now: 3
consumed: Available now: 2
produced: Available now: 3
produced: Available now: 4
consumed: Available now: 3
consumed: Available now: 2
consumed: Available now: 0
consumed: Available now: 0
produced: Available now: 2
produced: Available now: 2
consumed: Available now: 1
consumed: Available now: 0
produced: Available now: 1
produced: Available now: 2
consumed: Available now: 1
consumed: Available now: 0


when producer delay is lower:

produced: Available now: 11
consumed: Available now: 10
produced: Available now: 11
consumed: Available now: 10
produced: Available now: 11
produced: Available now: 12
consumed: Available now: 11
consumed: Available now: 10
produced: Available now: 11
produced: Available now: 12
produced: Available now: 13
produced: Available now: 12
consumed: Available now: 12
consumed: Available now: 11
produced: Available now: 13
produced: Available now: 14
consumed: Available now: 13
consumed: Available now: 12
produced: Available now: 13
produced: Available now: 14
produced: Available now: 15
produced: Available now: 16
consumed: Available now: 15
consumed: Available now: 14
produced: Available now: 15
produced: Available now: 16
consumed: Available now: 15
consumed: Available now: 14
produced: Available now: 15
produced: Available now: 16
produced: Available now: 17
consumed: Available now: 16
consumed: Available now: 17
produced: Available now: 18
produced: Available now: 17
produced: Available now: 18
consumed: Available now: 17
consumed: Available now: 16
produced: Available now: 17
produced: Available now: 18
consumed: Available now: 16
consumed: Available now: 17
produced: Available now: 17
produced: Available now: 18
produced: Available now: 19
produced: Available now: 20
consumed: Available now: 19
consumed: Available now: 18
produced: Available now: 19
produced: Available now: 20
consumed: Available now: 18
produced: Available now: 19
consumed: Available now: 18
produced: Available now: 20
produced: Available now: 21
produced: Available now: 22
consumed: Available now: 21
consumed: Available now: 20
produced: Available now: 21
produced: Available now: 22
consumed: Available now: 21
consumed: Available now: 20
produced: Available now: 21
produced: Available now: 22
produced: Available now: 23
produced: Available now: 24
consumed: Available now: 23
consumed: Available now: 22
produced: Available now: 23
produced: Available now: 24


What is wrong in the program?

Answer

First, you create a Semaphore with 10 initial permits, as explained here. It is not an upper limit, so release() can make the number of available permits go above 10.

Also, look at the code below:

synchronized (this.lock) {
    s.acquire(); // consume resource
}

You acquire the lock. If there are no resources available, the thread pauses, but does not release the lock. Thus, the below code cannot acquire the lock and cannot execute. Consumer waits for a permit and producer waits for the lock, resulting in a deadlock.

synchronized (this.lock) {
    s.release(); // produce an resource
}

You can solve this problem by not synchronizing s.acquire() and s.release().

Comments