Читатели-писатели с мониторами - Java

Я реализую проблему читателей писателей с мониторами в Java. Есть много читателей и писателей. Когда писатель пишет, никакой другой читатель или писатель не может читать или писать. Многие читатели могут читать одновременно. Я не знаю, что не так с этим кодом. Есть тупиковая проблема.

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();
    }
}

2 ответа

Проблема на самом деле довольно проста: все ваши Start_Write, End_Write, Start_Read, End_Read методы отмечены как synchronized. У вас есть только один экземплярMonitorв вашей программе. Думать оsynchronizedкак эксклюзивная блокировка, которая по умолчанию прикрепляется к каждому объекту Java. В любой синхронизированный метод, принадлежащий этому объекту, может одновременно выполняться только один поток. Блокировка снимается только при возврате метода.

Рассмотрим следующую последовательность событий:

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

Вот и ваш тупик.

  • Читатель не может снять блокировку без выхода из Start_Read
  • Чтобы выйти из Start_Read, он ждет, пока Writer вызовет End_Write
  • Writer не может вызвать End_Write, потому что для этого требуется блокировка

Решение вашей проблемы довольно простое: отбросьте всю эту настраиваемую логику и используйте ReentrantReadWriteLock который предоставляется JDK и специально разработан для решения вашей проблемы.

ReetrantReadWriteLock.readLock().lock() эквивалентно Monitor.Start_Read()ReetrantReadWriteLock.readLock().unlock() эквивалентно Monitor.End_Read()ReetrantReadWriteLock.writeLock().lock() эквивалентно Monitor.Start_Write()ReetrantReadWriteLock.writeLock().unlock() эквивалентно Monitor.End_Write()

Еще один комментарий: вы всегда должны помещать код, снимающий блокировки, в finallyblock, чтобы убедиться, что в случае возникновения исключения ваше приложение не окажется в тупике. Например:

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();
            }
        }

    }
}

Вы должны получить значение для функции wait().

10 секунд ожидания:

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

Для меня это прекрасно работает после изменения этого.

Я положил sleep метод внутри вашего кода. Пожалуйста, проверьте это и попробуйте.

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();     
} }
Другие вопросы по тегам