简体   繁体   中英

Reader Writer implementation in java

I was trying to implement a reader-writer using notify and wait. But i think I'm stuck. My sequence goes like this. RRRRRRRRRRWWWWWWWWW This happens if the main start with reader invoked first. Or WWWWWWWRRRRRRRRRRR . This happens if the main start with the writer invoked first. Looks like reads notify isn't working at all. Writer thread never goes into execution.

If i make while loop in run method to run infinite then it's just RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR........ . No chance for the writer to write. Can you have a look at this?

DATA CLASS

public class Data {
    private int q ;
    private boolean isAnyOneReading;

    public Data() {
    }

    public  void readQ() {
        synchronized (this){
            isAnyOneReading = true;
            System.out.println("Read start "+q);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized (this){
            isAnyOneReading = false;
            System.out.println("Read end "+q);
            notifyAll();
        }
    }

    public synchronized void writeQ(int q) {
        System.out.println(isAnyOneReading);
        while (isAnyOneReading){
            try{
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("Done");
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("Write start "+q);
        this.q = q;
        try{
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
        System.out.println("Write end "+q);
        notifyAll();
    }
}

READER CLASS

public class Reader implements  Runnable {
    private Data data;
    private Thread readerThread;


    public Reader(Data data) {
        this.data = data;
        readerThread = new Thread(this, "ReaderThread");
    }


    void startThread(){
        readerThread.start();
    }

    @Override
    public void run() {
        int i = 0 ;
        while (i != 5){
            data.readQ();
            i++;
        }
    }
}

WRITER CLASS

public class Writer  implements  Runnable{
    private Data data;
    private Thread writerThread;

    public Writer(Data data) {
        this.data = data;
        writerThread = new Thread(this,"WriterThread," );
    }

    void startThread(){
        writerThread.start();
    }

    @Override
    public void run() {
        int i = 0 ;
        int j = 0 ;
        while (j != 5){
            data.writeQ(i++);
           // i++;
           j++;
        }
    }
}

MAIN CLASS

public class ReaderWriterDemo {
    public static void main(String[] args) {
        Data data = new Data();
        Reader reader = new Reader(data);
        Writer writer = new Writer(data);

        reader.startThread();
        writer.startThread();



    }
}

Try removing the Thread.sleep from Data class. And add Thread.sleep in run methods like so. (pasting one example):

 @Override
public void run() {
    int i = 0;
    while (i != 5) {
        data.readQ();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            i++;
        }
    }
}

Read's notifyAll() works, but it seems that read() re-called again and changes isAnyOneReading's value prior to any other action in write(). That's why check fails and write() starts waiting again. As Danny Fried suggested moving Thread.sleep() to the run methods will help.

Looks like a simple case of starvation. Consider your writer's main loop:

    while (j != 5){
        data.writeQ(i++);
       // i++;
       j++;
    }

data.writeQ() is a synchronized method: The very last thing it does before it returns is to unlock the lock. The very first thing it does on the next call is to re-lock the lock. Not much happens in-between--increment and test a local variable is all.

Java synchronized locks are not fair . (ie, when a lock becomes available, the system does not guarantee that the winner will be the thread that's been waiting the longest.) In fact, it may be be the opposite of fair: The OS may try to maximize efficient use of the CPU(s) by always choosing the thread that's easiest to wake up.

When the writer comes back to call data.writeQ() on each subsequent iteration, it may be that the OS has not even have started to wake up the reader, and it simply lets the writer enter the synchronized block again.


Same thing happens with your reader. The code is a bit more complicated, but just like in the writer, the very last thing that data.readQ() does before returning is to unlock the lock, and the very first thing that it does on the next call is to lock it again.


Brute force solution: replace the synchronized blocks with a fair ReentrantLock object.

Alternate solution, which is more typical of how many programs actually work: Have the threads do something else (eg, have them do some I/O) in between calls to the locked function, thereby giving the other threads a chance to get in and use the locked resource.

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