Solution to Senate Bus Problem

This is Java Implementation of the Semaphore based solution for Senate Bus Problem

https://github.com/Rajind/SenateBusProblem

The Senate Bus Problem (From The Little Book of Semaphores by Allen B. Downey)

This problem was originally based on the Senate bus at Wellesley College. Riders come to a bus stop and wait for a bus. When the bus arrives, all the waiting riders invoke boardBus, but anyone who arrives while the bus is boarding has to wait for the next bus. The capacity of the bus is 50 people; if there are more than 50 people waiting, some will have to wait for the next bus. When all the waiting riders have boarded, the bus can invoke depart. If the bus arrives when there are no riders, it should depart immediately.


import java.util.Scanner;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author Rajind
 */
public class Simulation {
    
    private static final SharedData sharedData = new SharedData();
    
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);  
        System.out.print("Enter the number of Riders : ");  
        int rider_count = scanner.nextInt();
        System.out.println("");
        
        new Bus(sharedData).busActivity();
                
        for(int i=0; i < rider_count ; i++){
            new Rider(sharedData).start();
        }
    }
}

class SharedData {
    private final AtomicInteger waiting_riders_count = new AtomicInteger(0);
    private final Semaphore riders_mutex = new Semaphore(1);  
    private final Semaphore bus = new Semaphore(0);  
    private final Semaphore boarded = new Semaphore(0);

    public AtomicInteger getWaiting_riders_count() {
        return waiting_riders_count;
    }

    public Semaphore getRiders_mutex() {
        return riders_mutex;
    }

    public Semaphore getBus() {
        return bus;
    }

    public Semaphore getBoarded() {
        return boarded;
    }
}

class Rider extends Thread{
    private final SharedData sharedData;
    
    public Rider(SharedData sharedData) {
        this.sharedData = sharedData;
    }
        
    @Override
    public void run(){
       this.runMethod();
    }
    
    private void runMethod(){
        try {
            /*
            New rider that comes to the bus stop waits on the riders mutex 
            to enter the queue (to update waiting_riders_count value)
            */
            sharedData.getRiders_mutex().acquire();
            /*
            New rider acuires the riders mutex
            */
            System.out.println("Riders mutex aquired by a Rider");
            
        
        } catch (InterruptedException ex) {
            Logger.getLogger(Rider.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        sharedData.getWaiting_riders_count().incrementAndGet();
        /*
        New rider enters the queue by incrementing waiting_riders_count value    
        */
        
        sharedData.getRiders_mutex().release();
        /*
        New rider releases the riders mutex after entering the queue
        */
        
        System.out.println("Riders mutex released by Rider");
        
        try {
            /*
            Now the rider wait to aqcuire the bus semaphore to enter the bus.
            It has to wait till Bus signals to acquire the lock.
            */
            sharedData.getBus().acquire();
            /*
            Rider is able to acquire the bus semaphore
            */
            
            System.out.println("Bus semaphore acquired by a Rider");
            
        } catch (InterruptedException ex) {
            Logger.getLogger(Rider.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("Rider boards bus");
        sharedData.getBoarded().release();
        /*
        Rider signals that he has boarded
        */
        System.out.println("Boarded semaphore released by Rider");
    }
}

class Bus implements Runnable{
    //number of passengers bus can take in one turn
    private final int SEAT_LIMIT = 50;
    
    //initial time before bus comes to the station
    private final int TERMINATION_TIME = 60;  //60 seconds

    //time delay after completion of one bus turn
    private final int DELAY_AFTER_ONE_TURN = 2; //2 seconds
	
	//initial time delay for the Bus
	private final int INITIAL_DELAY = 1; //1 seconds
    
    private final SharedData sharedData;
    private final ScheduledExecutorService scheduler;
    
    public Bus(SharedData sharedData) {
        this.scheduler = Executors.newScheduledThreadPool(1);
        this.sharedData = sharedData;
    }
    
    public void busActivity(){
     final Runnable senateBus = new Runnable() {
       public void run() {
           runMethod();
       }
     };
     
     /*executes a periodic action that becomes enabled first after the given 
     initial delay, and subsequently with the given delay between the termination 
     of one execution and the commencement of the next.
     */
     final ScheduledFuture<?> busHandle =
       scheduler.scheduleWithFixedDelay(senateBus, INITIAL_DELAY, DELAY_AFTER_ONE_TURN, TimeUnit.SECONDS);
     
     //executes a schedule future after a given delay
     scheduler.schedule(new Runnable(){ 
         public void run() {
             busHandle.cancel(true);
             System.exit(0);
         }
     }, TERMINATION_TIME, TimeUnit.SECONDS);
    }

    @Override
    public void run() {
        this.runMethod();
    }
    
    private void runMethod(){
        try {
            /*
            Bus waits to acquire riders mutex so that it can start boarding
            */
            sharedData.getRiders_mutex().acquire();
            /*
            Bus acquired writers lock. So now no other Rider can enter the queue
            till the Bus releases the lock
            */
            System.out.println("Riders mutex aquired by Bus");
            //System.out.println("Bus came to the stop & locked riders_count");
        } catch (InterruptedException ex) {
            Logger.getLogger(Bus.class.getName()).log(Level.SEVERE, null, ex);
        }

        int boarding_riders_count = Math.min(sharedData.getWaiting_riders_count().get(), SEAT_LIMIT);
        /*if there are riders more than the value of SEAT_LIMIT waiing then 
            we set r_count to SEAT_LIMIT
        */

        System.out.format("\nWaiting riders count: %d\n",sharedData.getWaiting_riders_count().get());
        System.out.format("\nBoarding riders count: %d\n", boarding_riders_count);

        for(int i=0; i < boarding_riders_count; i++){
            /*
            Bus signals riders that one of them can enter
            */
            sharedData.getBus().release();
            System.out.println("Bus semaphore released by Bus");
            try {
                sharedData.getBoarded().acquire();
                /*
                Bus aquires boarded sepahore to say rider has boarded
                */
                System.out.println("Boarded semaphore aquired by Bus");
            } catch (InterruptedException ex) {
                Logger.getLogger(Bus.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        int n = sharedData.getWaiting_riders_count().get();
        sharedData.getWaiting_riders_count().set(Math.max(n - 50, 0));
        
        /*
        if(sharedData.getWaiting_riders_count().get() == 0){
            System.exit(0);
        }*/
        
        sharedData.getRiders_mutex().release();
        /*
        Bus updates waiting queue count and releases lock
        */
        System.out.println("Riders mutex released by Bus");
        System.out.println("Bus Departs");
    }
}

Class “Simulation” contains the main method.

If you are using the command prompt :
1) compile it using : javac Simulation.java
2) run it using : java Simulation

You will be prompted to enter a integer parameter to rider count.

Using paramater in Class Bus,
Program is set to terminate after 60 seconds.(TERMINATION_TIME)

Delay after one bus turn is set to 2 seconds. (DELAY_AFTER_ONE_TURN)

Intial delay for bus arrival is set to 1 seconds. (INITIAL_DELAY)

~Rajind Ruparathna

Leave a comment