简体   繁体   中英

Readers-writers with monitors - java

I am implementing readers writers problem with monitors in Java. There are many readers and writers. When a writer is writing, no other reader or writer can read or write. Many readers can read simultaneously. I don't know what's wrong with this code. There is deadlock problem.

class Monitor {
    private int readers; // specifies number of readers reading
    private boolean writing; // specifies if someone is writing
    private Condition OK_to_Read, OK_to_Write;

    public Monitor() {
        readers = 0;
        writing = false;
        OK_to_Read = new Condition();
        OK_to_Write = new Condition();
    }

    public synchronized void Start_Read(int n) {

        System.out.println("wants to read " + n);
        if (writing || OK_to_Write.is_non_empty()) {
            try {
                System.out.println("reader is waiting " + n);
                OK_to_Read.wait_();
            } catch (InterruptedException e) {
            }
        }
        readers += 1;
        OK_to_Read.release_all();

    }

    public synchronized void End_Read(int n) {

        System.out.println("finished reading " + n);
        readers -= 1;

        if (OK_to_Write.is_non_empty()) {
            OK_to_Write.release_one();
        } else if (OK_to_Read.is_non_empty()) {
            OK_to_Read.release_one();
        } else {
            OK_to_Write.release_all();
        }

    }

    public synchronized void Start_Write(int n) {
        System.out.println("wants to write " + n);
        if (readers != 0 || writing) {
            try {
                System.out.println("Writer is waiting " + n);
                OK_to_Write.wait_();
            } catch (InterruptedException e) {
            }
        }

        writing = true;

    }

    public synchronized void End_Write(int n) {

        System.out.println("finished writing " + n);
        writing = false;
        if (OK_to_Read.is_non_empty()) {
            OK_to_Read.release_one();
        } else if (OK_to_Write.is_non_empty()) {
            OK_to_Write.release_one();
        } else {
            OK_to_Read.release_all();
        }

    }

}

class Condition {
    private int number;// specifies the number of readers/writers waiting

    public Condition() {
        number = 0;
    }

    public synchronized boolean is_non_empty() {
        if (number == 0)
            return false;
        else
            return true;
    }

    public synchronized void release_all() {
        number = 0;
        notifyAll();
    }

    public synchronized void release_one() {
        number -= 1;
        notify();
    }

    public synchronized void wait_() throws InterruptedException {
        number++;
        wait();
    }

}

class Reader extends Thread {
    private Monitor M;
    private String value;

    public Reader(String name, Monitor c) {
        super(name);
        M = c;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            M.Start_Read(i);
            // System.out.println("Reader "+getName()+" is retreiving data...");
            System.out.println("Reader is reading " + i);
            M.End_Read(i);
        }

    }
}

class Writer extends Thread {
    private Monitor M;
    private int value;

    public Writer(String name, Monitor d) {
        super(name);
        M = d;
    }

    public void run() {
        for (int j = 0; j < 10; j++) {
            M.Start_Write(j);
            // System.out.println("Writer "+getName()+" is writing data...");
            System.out.println("Writer is writing " + j);
            M.End_Write(j);
        }

    }
}

class mainClass {
    public static void main(String[] args) {
        Monitor M = new Monitor();
        Reader reader = new Reader("1", M);
        Writer writer = new Writer("1", M);
        writer.start();
        reader.start();
    }
}

The problem is actually quite simple: All of your Start_Write , End_Write , Start_Read , End_Read methods are marked as synchronized . You have only one instance of Monitor in your program. Think of synchronized as an exclusive lock that is attached by default to each java object. There can only be exactly one thread at a time running in any synchronized method that belong to that object. The lock is only released when the method returns.

Consider the following sequence of event:

1. Writer enters Start_Write, and takes the lock on Monitor
2. Writer exits Start_Write, and releases the lock on Monitor
3. Reader enters Start_Read, and takes the lock on Monitor
4. Reader cannot exit Start_Read, because the writer is still writing.
   The lock on Monitor IS NOT RELEASED
5. Writer wants to enter End_Write, but the lock is not available because
   Reader is still holding it

There's your deadlock.

  • Reader can't release the lock without exiting Start_Read
  • To exit Start_Read, it waits for Writer to call End_Write
  • Writer cannot call End_Write because it requires the lock to do so

The solution to your problem is pretty easy: Drop all this custom logic and use ReentrantReadWriteLock which is provided by the JDK and is specifically designed to handle your problem.

ReetrantReadWriteLock.readLock().lock() is equivalent to Monitor.Start_Read() ReetrantReadWriteLock.readLock().unlock() is equivalent to Monitor.End_Read() ReetrantReadWriteLock.writeLock().lock() is equivalent to Monitor.Start_Write() ReetrantReadWriteLock.writeLock().unlock() is equivalent to Monitor.End_Write()

One more comment: You should always put code that releases locks into a finally block, to make sure that in case an Exception is thrown your application doesn't become deadlocked. For example:

class Writer extends Thread {
    private ReentrantReadWriteLock lock;
    private int value;

    public Writer(String name, ReentrantReadWriteLock lock) {
        super(name);
        this.lock = lock;
    }

    public void run() {
        for (int j = 0; j < 10; j++) {
            lock.writeLock().lock();
            try{
                // System.out.println("Writer "+getName()+" is writing data...");
                System.out.println("Writer is writing " + j);
            } finally {
                lock.writeLock().unlock();
            }
        }

    }
}

I have put sleep method inside your code. Please check this & try.

class Monitor
{
private volatile int readers; //specifies number of readers reading
private volatile boolean writing; //specifies if someone is writing
private volatile Condition OK_to_Read, OK_to_Write;

 public Monitor()
{
    readers = 0;
    writing = false;
    OK_to_Read = new Condition();
    OK_to_Write = new Condition();
}

public synchronized void Start_Read(int n)
{

     System.out.println("wants to read " + n);
    if(writing || OK_to_Write.is_non_empty())
    {
        try{
            System.out.println("reader is waiting " + n);
            OK_to_Read.sleep_();
        }
        catch(InterruptedException e){}
    }
    readers += 1;
    OK_to_Read.release_all();

}

public synchronized void End_Read(int n)
{

        System.out.println("finished reading " + n);
        readers -= 1;

        if(OK_to_Write.is_non_empty())
        {
            OK_to_Write.release_one();
        }
        else if(OK_to_Read.is_non_empty())
        {
            OK_to_Read.release_one();
        }
        else
        {
            OK_to_Write.release_all();
        }

}

public synchronized void Start_Write(int n)
{
    System.out.println("wants to write " + n);
    if(readers != 0 || writing)
    {
        try{
            System.out.println("Writer is waiting " + n);
            OK_to_Write.sleep_();
                }catch(InterruptedException e){}
    }

    writing = true;

}

public synchronized void End_Write(int n)
{

    System.out.println("finished writing " + n);
    writing = false;
    if(OK_to_Read.is_non_empty())
    {
        OK_to_Read.release_one();
    }
    else if(OK_to_Write.is_non_empty())
    {
        OK_to_Write.release_one();
    }
    else
    {
        OK_to_Read.release_all();
    }

}

}

class Condition
{
private int number;//specifies the number of readers/writers waiting

public Condition()
{ 
    number = 0; 
}

public synchronized boolean is_non_empty()  
{ 
    if(number == 0)
        return false; 
    else
        return true;
}

public synchronized void release_all()
{ 
number = 0;
notifyAll(); 
}


public synchronized void release_one()
{ 
number -=1;
notify(); 
}   

public synchronized void wait_() throws InterruptedException
{  
    number++;
    wait();
}
public synchronized void sleep_() throws InterruptedException
{  
    Thread.sleep(1000);
}

}


class Reader extends Thread
{
private Monitor M;
private String value;
public Reader(String name,Monitor c)
{
    super(name);
    M=c;
}

public void run()
{
    for(int i = 0; i < 10; i++){
            M.Start_Read(i);
            //System.out.println("Reader "+getName()+" is retreiving data...");
            System.out.println("Reader is reading " + i);
            M.End_Read(i);
    }

}
}

class Writer extends Thread
{
private Monitor M;
private int value;
public Writer(String name, Monitor d)
{
    super(name);
    M = d;
}

public void run()
{
    for(int j = 0; j < 10; j++){
            M.Start_Write(j);
            //System.out.println("Writer "+getName()+" is writing data...");
            System.out.println("Writer is writing " + j);
            M.End_Write(j);
    }

}
}

public class Demo
{
public static void main(String [] args)
{
    Monitor M = new Monitor();
    Reader reader = new Reader("1",M);
    Writer writer = new Writer("1",M);
    writer.start();
    reader.start();     
} }

You should set a value to the wait() function.

For 10 seconds waiting:

public synchronized void wait_() throws InterruptedException {
        number++;
        wait(10000);
    }

For me it works fine after modifying this.

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