Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java unisex bathroom

I have to solve this problem using Java semaphores, but I have no idea how, and I cannot find any related Java materials. This is how it goes:

There are to kinds of threads: men and women. Both wants to use same resources which quantity is BATHROOM_SIZE. 5 rules:

  1. Every thread, after signaling need of using resource, should wait until he will be able to use it.
  2. Prevent situation, when more than BATHOOM_SIZE threads is using resource concurrently.
  3. Prevent woman and man use bathoom in the same time.
  4. Threads should use resources concurrently. If there are many threads of one type, up to BATHROOM_SIZE threads should use resource.
  5. Prevent starvation.

Results

Works for:

1woman, 1man, 5women, 5men

Fails for:

5women1men, 5men1women, 2men2women, 5men5women.

I've been trying to make it work since Monday and now I've run out of ideas.

Code

So my task is to write Bathroom.java class which implements BathroomInterface:

public interface BathroomInterface {

    public static final int BATHROOM_SIZE = 3; //3 is just example
    void manEnter();
    void manExit();
    void womanEnter();
    void womanExit();
}

In system there are a number of man and woman threads which work like this:

for(int i = 0; i < n; i++) {
  bathroom.manEnter();
  //uses bathroom random amount of time
  bathroom.manExit();
}

for(int i = 0; i < m; i++) {
  bathroom.womanEnter();
  //uses bathroom random amount of time
  bathroom.womanExit();
}

I also have scheme of Bathroom.java class, I have to extend:

import java.util.concurrent.Semaphore;

public class Bathroom implements BathroomInterface {

    
    private Semaphore mutex = new Semaphore(1, true);

    public void womanEnter() {
        mutex.acquireUninterruptibly();
    }

    public void womanExit() {
        mutex.release();
    }

    public void manEnter() {
        mutex.acquireUninterruptibly();        
    }

    public void manExit() {
        mutex.release();
    }
}

This is what I made so far:

import java.util.concurrent.Semaphore;

public class Bathroom implements BathroomInterface {
    int manW=0, manU=0, womanW=0, womanU=0; //*U-using, *W-waiting
    private Semaphore mutex = new Semaphore(1, false);

    public void womanEnter() {
        womanW++;
        StateChange();
    }

    public void womanExit() {
        womanU--;
        mutex.release();
        StateChange();
    }

    public void manEnter(){
        manW++;
        StateChange();
    }

    public void manExit() {
        manU--;
        mutex.release();
        StateChange();
    }
    
    void StateChange() {
        if(womanU==0 && manU==0) {
            if(manW>womanW) {
                while(manW>0 && manU<BATHROOM_SIZE) {
                    manW--;
                    manU++;
                    mutex.acquireUninterruptibly();
                }
            }
            else {
                while(womanW>0 && womanU<BATHROOM_SIZE) {
                    womanW--;
                    womanU++;
                    mutex.acquireUninterruptibly();
                }
            }
        }
        if(womanU==0 && manU<BATHROOM_SIZE) {
            while(manW>0 && manU<BATHROOM_SIZE) {
                manW--;
                manU++;
                mutex.acquireUninterruptibly();
            }
        }
        if(manU==0 && womanU<BATHROOM_SIZE) {
            while(womanW>0 && womanU<BATHROOM_SIZE) {
                womanW--;
                womanU++;
                mutex.acquireUninterruptibly();
            }
        }
    }
}
like image 589
BlackSlash Avatar asked Jun 21 '12 09:06

BlackSlash


Video Answer


2 Answers

Actually this exercise is done using a monitor, and not a semaphore. What you're doing is mostly fine, you're missing the conditions. So, in your bathroom class, declare:

a lock:

private Lock lock = new ReentrantLock();

2 conditions or queues, attached to your lock:

private Condition womenWaitingQueue = lock.newCondition();
private Condition menWaitingQueue = lock.newCondition();

2 counters to know how many are waiting, and 2 to know how many are using:

private int womenWaitingN = 0;
private int menWaitingN = 0;
private int womenUsingN = 0;
private int menUsingN = 0;

and of course, the number of resources:

private final int BATHROOM_CAPACITY = 5;
private int free_resources = BATHROOM_CAPACITY;

all 4 functions were here, but removed because of the homework tag

The important thing here is to prevent starvation, by not allowing any men to enter the bathroom if there are women waiting and viceversa.

so, conditions are that if a man wants to enter to the bathroom, it has to check if the bathroom has at least 1 free spot (using free resources) and if there are women in the bathroom (using womenUsingN). If any of these 2 conditions are not met, the man must wait(using the menWaitingQueue):

menWaitingQueue.await();

when a man leaves the bathroom, it has to check if there are any women waiting (womenWaitingN), if there are, they get notified:

womanWaitingQueue.signal();

because of the menUsingN counter, women signaled by this wont be able to enter until there are no men in the bathroom. If there are no women waiting, then a man can be signaled to enter the bathroom. This prevents starvation because priority is given to the opposite sex (if waiting).

The last thing, is that every function must lock/unlock the lock at beginning/end of each enter/exit function.

lock.lock();
lock.unlock();

I think with this new information you'll be able to make the functions on your own. Good luck!

like image 123
Th0rndike Avatar answered Oct 07 '22 23:10

Th0rndike


I think you struggle with the whole mutex.acquire and mutex.release semantics, especially with what the mutex is actually supposed to guard. Let me try to simplify the problem a little to give you a hint as to how to approach this.

You are asked to implement a concurrency object that's more complicated than a simple semaphore, with two client classes and starvation prevention. I'm not going to do that for you, but i'm going to show you how a simple semaphore looked like in the pre-Java6 days:

public class Resource {

    private int numClients = 0;
    private final int maxClients;

    public Resource(int maxClients) {
        this.maxClients = maxClients;
    }

    public synchronized void acquire() {
        while (!clientCanAcquire()) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        ++numClients;
        printState();
    }

    public synchronized void release() {
        --numClients;
        printState();
        notify();
    }

    private boolean clientCanAcquire() {
        return numClients < maxClients;
    }

    private void printState() {
        System.out.println("Resource is currently acquired by " + numClients
                + " clients");
    }
}

A Client can access this as follows:

import java.util.Random;

public class Client implements Runnable {

    private Resource resource;
    private Random rnd = new Random();

    public Client(Resource resource) {
        this.resource = resource;
    }

    public void run() {
        try {
            Thread.sleep(rnd.nextInt(1000));
            resource.acquire();
            Thread.sleep(rnd.nextInt(1000));
            resource.release();
        } catch (InterruptedException e) {
        }
    }
}

and the simplest application that can drive the whole thing would look like this:

public class App {

    public static void main(String[] arg) {

        Resource r = new Resource(3);
        for (int i = 0; i < 10; i++) {
            Thread client = new Thread(new Client(r));
            client.start();
        }
    }
}

Resource stores the information it needs to determine when a client can access in internal variables. In a multithreaded application, access to these variables must be synchronized. Shown here is the simplest way to do this, but you could also say

private Object mutex = new Object();

and then

synchronized (mutex) { }

or any other type of mutex.

Your problem is more complicated than a simple ordinary semaphore, but the underlying logic should be pretty similar.

like image 32
wallenborn Avatar answered Oct 07 '22 23:10

wallenborn