Search code examples
javamultithreadingconcurrencyjava.util.scannerjava-threads

How can I get threads to do action based on condition provided by Scanner?


Thank you so much for taking a look at my question, I'm very confused and frustrated by a problem that probably has an easy solution, I would really appreciate any help.

All I want is to have two instances of of a Barrier that is either closed or open, the way I want to determine if the barrier should be open is via a scanner, so if I click 1, I should get the message "Entrance Barrier " + threadid + " Opened"

  public class MainClass{
    public static void main(String[] args) {

      EntranceBarrier e1 = new EntranceBarrier(); //Entrance Barrier 1
      EntranceBarrier e2 = new EntranceBarrier(); //Entrance Barrier 2

        e1.start();
        e2.start();

        System.out.println("Open?");
        Scanner sc = new Scanner(System.in);
        int operation = sc.nextInt();


       while(operation != 0){

          switch (operation) {
              case 1:
                  e1.setOpen(true);
                  e2.setOpen(true);
                  System.out.println("Opt. 1 Working");
                  break;
              case 2:
                  e1.setOpen(false);
                  e2.setOpen(false);
                  System.out.println("Opt. 2 Working");
                  break;
              default:
                  System.out.println("NOPE");
                  break;
          }

         System.out.println("Open?");
         operation = sc.nextInt();
       }


    }
   }

Whenever I run the main method it first prints out "Entrance Barrier Closed" and when I input 1 to the scanner it only prints out "Opt. 1 Working" meaning it didn't change setOpen() to true.

Here's the EntranceBarrier class:

import java.io.*;
import java.util.Scanner;

public class EntranceBarrier extends Thread {

    private volatile boolean open = false;

    public synchronized void OpenBarrier(){
     if(isOpen()){
        try{
          String threadid = Thread.currentThread().getName();
          System.out.println("Entrance Barrier " + threadid + " Opened");
          Thread.sleep(5000);
        }catch(InterruptedException e){
           e.printStackTrace();
        }
       } else{
          System.out.println("Entrance Barrier Closed");

       }

    }

    public void run() {
       OpenBarrier();
    }

    public boolean isOpen() {
        return open;
    }

    public void setOpen(boolean open) {
        this.open = open;
    }

}

I don't know if this question is clear enough, I really hope you can help me out, it's being tearing me up D:


Solution

  • I would like to suggest some changes.

    First i would like to name all entrance threads:

    import java.util.Scanner;
    
    public class MainClass {
    public static void main(String[] args) {
    
        EntranceBarrier e1 = new EntranceBarrier(); // Entrance Barrier 1
        e1.setName("Bar1");
        EntranceBarrier e2 = new EntranceBarrier(); // Entrance Barrier 2
        e2.setName("Bar2");
    
        e1.start();
        e2.start();
    
        System.out.println("Open?");
        Scanner sc = new Scanner(System.in);
        int operation = sc.nextInt();
    
        while (operation != 0) {
            switch (operation) {
            case 1:
                e1.setOpen(true);
                e2.setOpen(true);
                System.out.println("Opt. 1 Working");
                break;
            case 2:
                e1.setOpen(false);
                e2.setOpen(false);
                System.out.println("Opt. 2 Working");
                break;
            default:
                System.out.println("NOPE");
                break;
            }
    
            System.out.println("Open?");
            operation = sc.nextInt();
        }
    }
    }
    

    Next i would like to add a loop to make active checks if change of state of an entrance thread was requested and variable for holding this information.

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class EntranceBarrier extends Thread {
    
        private volatile boolean open = false;
        private volatile boolean requestedState = false;
    
        private Lock lock = new ReentrantLock();
    
        public void OpenBarrier() {
            try {
                lock.lock();
                printState();
                lock.unlock();
    
                while (true) {
                    lock.lock();
                    if (requestedState != open) {
                        printState();
    
                        Thread.sleep(5000);
                        open = requestedState;
    
                        printState();
                    }
                    lock.unlock();
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                lock.unlock();
            }
        }
    
        private void printState() {
            if (requestedState == open) {
                if (open)
                    System.out.println("Entrance Barrier " + getName() + " Opened");
                else
                    System.out.println("Entrance Barrier " + getName() + " Closed");
            } else {
                if (requestedState)
                    System.out.println("Entrance Barrier " + getName() + " Opening");
                else
                    System.out.println("Entrance Barrier " + getName() + " Closing");
            }
        }
    
        public void run() {
            OpenBarrier();
        }
    
        public boolean isOpen() {
            return open;
        }
    
        public void setOpen(boolean open) {
            if (lock.tryLock()) {
                this.requestedState = open;
                lock.unlock();
            } else {
                System.out.println("Entrance Barrier " + getName() + " is moving");
            }
        }
    }
    

    I have used reentrant lock instead of synchronized keyword to be able to make checks if lock is already locked. It enables setOpen function to exit if change of state of an entrance is in progress.

    EntranceBarrier can be further upgraded with wait()/notify(). Active waiting can be replaced with more efficient way.

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class EntranceBarrier extends Thread {
    
        private volatile boolean open = false;
        private volatile boolean requestedState = false;
    
        private Lock lock = new ReentrantLock();
    
        public void OpenBarrier() {
            try {
                lock.lock();
                printState();
                lock.unlock();
    
                while (true) {
                    // waiting for state change request
                    synchronized (this) {
                        wait();
                    }
                    lock.lock();
                    if (requestedState != open) {
                        printState();
    
                        Thread.sleep(5000);
                        open = requestedState;
    
                        printState();
                    }
                    lock.unlock();
                    // Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                lock.unlock();
            }
        }
    
        private void printState() {
            if (requestedState == open) {
                if (open)
                    System.out.println("Entrance Barrier " + getName() + " Opened");
                else
                    System.out.println("Entrance Barrier " + getName() + " Closed");
            } else {
                if (requestedState)
                    System.out.println("Entrance Barrier " + getName() + " Opening");
                else
                    System.out.println("Entrance Barrier " + getName() + " Closing");
            }
        }
    
        public void run() {
            OpenBarrier();
        }
    
        public boolean isOpen() {
            return open;
        }
    
        public void setOpen(boolean open) {
            if (lock.tryLock()) {
                this.requestedState = open;
                // notifying that state change was requested
                synchronized (this) {
                    notify();
                }
                lock.unlock();
            } else {
                System.out.println("Entrance Barrier " + getName() + " is moving");
            }
        }
    }