0

I have been trying to implement the reader writer problem in Java. But when the Thread.Sleep(5000) is executed, the current thread process is not interrupted.

For example, when the first reader has entered the database, and the process sleeps for 5 seconds, I want the second reader to enter the database. But in my code, the second reader only enters the database after the first reader wakes up and leaves the database

How do I achieve my desired result?

import java.util.Scanner;

class CriticalSec {
    static boolean mutex = true;
    static boolean db = true;
    static int readerCount = 0;
    static Writer arrWriter = new Writer();
    static Reader1 r1= new Reader1();
    static Reader2 r2= new Reader2();

    public void readerEnter() throws InterruptedException {
        if (readerCount==0){
            r1.enter();
        }else{
            r1.enter();
        }
        if (mutex) {
            mutex = false;
        }
        readerCount += 1;
        if (readerCount == 1) {
            if (db) {
                db = false;
            } else {
                System.out.println("\nReader cannot enter database.\n");
                System.out.println("Waiting for writer to exit....");
                wait();
            }
        }
        if (!mutex) {
            mutex = true;
            Thread.sleep(5000);
            if (readerCount==1){
                r1.exit();
            }else{
                r2.exit();
            }
        }

    }

    public void WriterEnter() throws InterruptedException {
        arrWriter.enter();
        if (db) {
            db = false;
            Thread.sleep(5000);
            arrWriter.exit();
            notify();

        } else {
            System.out.println("Writer cannot enter database.");
            System.out.println("Waiting for writer/reader to exit....");
            wait();
        }

    }

}

class Reader1 extends Thread {
    public void run() {
        System.out.println("New reader created.");
    }

    public void enter() throws InterruptedException {
        System.out.println("\nReader 1 has entered in the database...\n");
    }

    public void exit() {
        if (CriticalSec.mutex) {
            CriticalSec.mutex = false;
        }
        CriticalSec.readerCount -= 1;
        if (CriticalSec.readerCount == 0) {
            CriticalSec.db = true;
        }
        CriticalSec.mutex = true;
        System.out.println("The reader 1 has now left");
    }
}

class Reader2 extends Thread {
    public void run() {
        System.out.println("New reader created.");
    }

    public void enter() throws InterruptedException {
        System.out.println("\nReader 2 has entered in the database...\n");
    }

    public void exit() {
        if (CriticalSec.mutex) {
            CriticalSec.mutex = false;
        }
        CriticalSec.readerCount -= 1;
        if (CriticalSec.readerCount == 0) {
            CriticalSec.db = true;
        }
        CriticalSec.mutex = true;
        System.out.println("The reader 1 has now left");
    }
}

class Writer extends Thread {
    public void run() {
        System.out.println("New Writer created.");
    }

    public void enter() throws InterruptedException {
        System.out.println("Writer has entered in the database.");
    }

    public void exit() {
        CriticalSec.db = false;
        System.out.println("Writer has left the database.");
    }
}

public class RWProblem {
    public static void main(String[] args) throws InterruptedException {
        Scanner sc = new Scanner(System.in);
        CriticalSec c = new CriticalSec();
        c.readerEnter();
        c.readerEnter();
        c.WriterEnter();
    }
}

I am just starting to learn Java, and I am sorry if my question is vague. I happy to provide more details.

Edit:

After brushing up on some important concepts and a lot of practice, I came up with a solution. Could someone please take a look at it and tell me if its alright? How can I improve it?

class RW {
    boolean dbOccupied = false;
    int readerCount = 0;
    boolean writer=false;

    public void readerEnter() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (dbOccupied && readerCount == 0) {
                    System.out.println("Reader cannot read... Database Occupied");
                    wait();
                }
                readerCount++;
                dbOccupied = true;
                System.out.println("Reader " + readerCount + " is reading...");
//                Thread.sleep(1000);
            }
        }
    }

    public void readerExit() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (readerCount != 0) {
                    System.out.println("Reader " + readerCount + " is now exiting...");
                    readerCount--;
                }
                dbOccupied = false;
                notifyAll();
//                Thread.sleep(1000);

            }
        }
    }

    public void writerEnter() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (dbOccupied){
                    System.out.println("New writer cannot write... Database Occupied");
                    wait();
                }
                dbOccupied = true;
                writer=true;
                System.out.println("Writer is now writing.....");
//                Thread.sleep(1000);

            }
        }
    }

    public void writerExit() throws InterruptedException {
        while (true) {
            synchronized (this) {
                if (writer) {
                    System.out.println("Writer leaving database...");
                    writer=false;
                    dbOccupied = false;
                    notifyAll();
//        Thread.sleep(1000);
                }




            }
        }

    }
}

public class RW3 {
    public static void main(String[] args) throws InterruptedException {
        final RW rw= new RW();
        Thread t1= new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    rw.readerEnter();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    rw.readerExit();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3= new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    rw.writerEnter();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t4= new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    rw.writerExit();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
//        t1.setPriority(2);
//        t3.setPriority(10);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t1.join();
        t3.join();
        t2.join();
        t4.join();
    }
}

this is the first time im using stackoverflow for asking a question, and im blown away by the response!!!!! i absolutely love the community

rsaraf97
  • 13
  • 3
  • 3
    I cannot find any place in your code where you would start your threads. Please read e.g. this: https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html . Your code runs everything sequentially because you never start two threads. The `Threads` are just like absolutely common normal classes until you call their method `start()`. And last but not least, if you are "just starting to learn Java", I really doubt if you should start with threads :) Your whole question can be reduced to "why don't my threads start at all" :) – Honza Zidek Oct 11 '21 at 14:10
  • 3
    When you are “just starting to learn Java”, consider starting with an easier topic than multithreading. Your code lacks any thread safety, but thankfully it never starts a second thread anyway. Focus on the basics first, e.g. why you should not use `static` variables like this. Or why creating but not using a `Scanner` has no effect at all. – Holger Oct 11 '21 at 14:11
  • By the way… In modern Java, we rarely address the `Thread` class directly. Instead, use the Executors framework. – Basil Bourque Oct 11 '21 at 16:35
  • Can you explain you need in more detail? The thread count have to be limited only 2? What will have to happen after both reader threads done their execution? It might be better if you could explain your need step by step. – Kozmotronik Oct 11 '21 at 19:08
  • Thankyou guys for your advice, I have been trying to strengthen my basics and understand threading. I realized my approach to the problem was very wrong. I have come up with a new solution to my problem. I would be grateful if you guys could take a look. – rsaraf97 Oct 21 '21 at 21:27

1 Answers1

0

You call the readEnter method from the main thread and it put the main thread to sleep hence blocked. In order to put the work to the reader threads themselves you need to organize everything in main thread then fire the reader threads through the main thread. You must call the readEnter method only from within the reader threads. I did not implemented the writer you are encouraged to do that. The order in which the threads start is not guaranteed since it depends the schedular. See the example code:


class CriticalSec {
    private int readerCount = 0;

    public boolean readerEnter() throws InterruptedException {
        if(readerCount == 0) {
            readerCount++;
            Thread.sleep(5000);
            return true;
        }
        else if(readerCount == 1) {
            readerCount++;
            Thread.sleep(5000);
            readerCount = 0; // rewind to zero
            return true;
        }
        else {
            System.out.println("Both reader threads are busy, "+Thread.currentThread().getName()+" will not be executed");
            return false;
        }
    }

}

class Reader implements Runnable {

    private final CriticalSec cs;

    public Reader(CriticalSec criticalSec) {
        cs = criticalSec;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" goes for reading...");
        try {
            String state = cs.readerEnter() ? " done reading" : " failed reading!";
            System.out.println(Thread.currentThread().getName()+state);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class RWProblem {
    public static void main(String[] args) {
        CriticalSec c = new CriticalSec();

        Reader r1 = new Reader(c);
        Reader r2 = new Reader(c);
        Reader r3 = new Reader(c);

        new Thread(r1, "Reader 1").start();
        new Thread(r2, "Reader 2").start();
        new Thread(r3, "Reader 3").start();
    }
}

The output is for one of the run:

Reader 3 goes for reading...
Reader 1 goes for reading...
Reader 2 goes for reading...
Both reader threads are busy, Reader 1 will not be executed
Reader 1 failed reading!
Reader 3 done reading
Reader 2 done reading

Process finished with exit code 0
Kozmotronik
  • 2,080
  • 3
  • 10
  • 25
  • Thankyou so much! This is a brilliant solution, certainly many new things to learn. – rsaraf97 Oct 21 '21 at 21:29
  • Can you explain to me why you used the Runnable interface and not the Thread class? Which is preferred? Do they both have different use cases? – rsaraf97 Oct 21 '21 at 21:29
  • It's all about resability. When you write code, your code must use as low memory as it can. So you need to prevent duplicate patterns. If you have a few pieces of code, and you see that all those different peaces make the same work, then you need to unite them into one reusable pattern in order to reduce program size and memor usage. – Kozmotronik Oct 22 '21 at 06:02
  • As per my previous comment, I created one Runnable **interface** which has a common reader pattern, then I used it with multiple threads. If I would create thread classes for each job that has the same pattern, there would be many duplicate code. Also `Thread` is a **class** while `Runnable` is an **interface** hence they are not same. A `Thread` represents a new **path** of work while a `Runnable` represents the **work itself**. So we put the work on to its new executing path in this case. – Kozmotronik Oct 22 '21 at 06:11
  • If you've studied computer architechture you can think of each thread as a virtual CPU core. More core, more parallel execution. – Kozmotronik Oct 22 '21 at 06:14