简体   繁体   中英

Monitor program using semaphore does not work as expected in java

I am trying to create a multiple producer multiple consumer problem's solution(ie 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?

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() .

This is what will happen

    void producer() {
        try {
            while (true) {

                //***All producers are stuck here because "first consumer" is having the lock.
                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) {
                //***All other consumers are stuck here because of line below.
                synchronized (this.lock) {
                    //***"First consumer" is stuck here waiting s.release()
                    s.acquire(); // consume resource
                }
                System.out.println("consumed:\tAvailable now:\t" + s.availablePermits());
                Thread.sleep(consumerDelay);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM