JAVA HELP PLEASE WITH CONCURRENCY import java.util.HashSet; import java.util.Lin
ID: 3707399 • Letter: J
Question
JAVA HELP PLEASE WITH CONCURRENCY
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Defined below are several abstract classes that define the backbone of an
* elevator system. You are to extend these abstract classes, implementing
* the abstract methods such that all the Persons are delivered safely to their
* destination floors.
*
* Start by making classes that will extend the abstract classes here. Your classes
* should be called:
* - Elevator
* - Button
* - ElevatorController
* Add method stubs in your concrete classes for each of the abstract methods, and
* go from there. Note that you should not need to import 'Provided'. Eclipse
* sometimes automatically imports it. Instead, when you refer to classes in Provided,
* reference them as "Provided._____".
*
* Read the comments in this file for instructions, advice, and hints on specific
* classes and methods.
*
* We strongly recommend that you implement the Elevator 'start()' method first!
* Try running the main method in this file after you do. You should see helpful
* debug information in your output console. We recommend that you then proceed to
* complete the Button class, then the Elevator class, and finally the ElevatorController
* class last.
*
* The abstract classes below include several public and protected fields. You may
* introduce more fields in your concrete implementations if you like, but you should
* not need to; the provided fields are sufficient. Neglecting the provided fields
* may cause tests to fail and/or debug information to be incorrect.
*
* We also recommend that you test your
* implementation with various experimental parameters. Make sure your implementation
* is thread-safe by trying high-speed, stressful parameters, like 'DELAY' = 5 and
* 'TEST_DURATION' = 1. Make sure your implementation works with any number of elevators
* and floors. You must utilize every elevator.
*
* You should not make any changes to this file except for testing purposes. Your
* implementation must work with the provided code.
*
* This assignment assumes a simplified elevator system in which each floor only
* has a single button (not 'going-up' and 'going-down' buttons), and for testing
* purposes, each floor starts with only has a single person on it. Elevators
* can only carry one person at a time.
*
*/
public final class Provided {
private static final Random random = new Random();
private static boolean passed = true;
/**
* Signal that is set to 'true' if the simulation should halt. All threads'
* run() methods should respect this signal by returning if set to 'true'.
*/
protected static boolean TERMINATE = false;
// ----- Experimental Parameters ----- \
/*
* Change these fields to adjust the parameters of the experiment. You might
* want to stress test your implementation by setting TEST_LENGTH to 1.
*/
/**
* Number of floors in the building. Floors are 0-indexed.
*/
public static final int FLOORS = 25;
/**
* Number of Elevators. Elevator 'id's go 'A', 'B', etc.
*/
public static final int ELEVATORS = 5;
/**
* The number of seconds during which buttons are randomly pressed.
* After this time, no buttons will be pressed, and the elevators only
* have to clear the queue.
*/
public static final int TEST_LENGTH = 30; // seconds
/**
* Essentially the inverse of the simulation speed. A delay of 1000 ms
* means that elevator floors will update once a second (1000 ms).
*/
public static final int DELAY = 500; // milliseconds
// ----- Exception Classes ----- \
/**
* This Exception should be thrown if a busy elevator is hailed.
*
* Note that it extends Exception, which means it is checked.
*/
public static class OccupiedException extends Exception {
private static final long serialVersionUID = -3388816891645794348L;
public OccupiedException(String id) {
super(id);
}
}
/**
* A TestFailure will be thrown if an elevator or the elevator control
* attempts an illegal operation or fails to meet requirements.
*
* Note that TestFailure extends RuntimeException, so it is unchecked.
*/
public static final class TestFailure extends RuntimeException {
private static final long serialVersionUID = 7177872724706310332L;
public TestFailure(String id) {
super(id);
TERMINATE = true;
passed = false;
}
}
// ----- Abstract Classes ----- \
/**
* An abstract class defining the backbone of any Elevator.
* You should extend this class. Make sure to use the provided fields.
*
* run() is implemented for you, and it will call the methods that you
* will implement.
* Lock provided
*
*/
public static abstract class AbstractElevator implements Runnable {
private static char ID = 'A';
protected ReentrantLock lock = new ReentrantLock();
protected Thread thread;
protected AbstractElevatorController control;
private int floor;
/**
* The elevator will move towards this floor, so make sure to set it appropriately
* in your concrete methods.
*/
protected int targetFloor;
public final char id;
/**
* The passenger field should be set upon being hailed. Make the passenger
* board the elevator when appropriate by calling 'board' on the field.
* Make the passenger get off the elevator by calling 'exit', then forget about
* the passenger by setting the field to null.
*/
protected Person passenger;
/**
* Field to allow the elevator to know whether it has picked up its passenger yet.
* The field is used in the provided toString method, which is in turn used in
* the printState method, so we recommend that you set this field appropriately.
*/
protected boolean carrying;
/**
* Note that when extending this class, you will have to wrap this constructor.
* Something like this:
*
* public Elevator(Provided.AbstractElevatorController c) {
* super(c);
* }
*
* Same goes for the other classes you are extending.
*
* @param control the controller for this elevator
*
*/
public AbstractElevator(AbstractElevatorController control) {
this.control = control;
this.targetFloor = this.floor = 0;
carrying = false;
passenger = null;
id = ID;
ID++;
}
public final int getFloor() {
return floor;
}
public final boolean isMoving() {
return floor != targetFloor;
}
/**
* This run() method will:
* 1. move the elevator up or down towards the targetFloor.
* 2. check if shouldPickUp()
* if yes, it will call pickUp()
* 3. check if shouldOffload()
* if yes, it will call offload()
* 4. repeat
*/
@Override
public final void run() {
int oldFloor = floor;
while (!TERMINATE) {
try {
lock.lock();
validateFloor(oldFloor);
if (id == 'A')
control.printState();
if (floor != targetFloor) { // move the elevator 1 floor
floor += (floor < targetFloor) ? 1 : -1;
}
if (shouldPickUp())
pickUp();
else if (shouldOffload())
offload();
oldFloor = floor;
} finally {
lock.unlock();
}
delay();
}
}
private final void validateFloor(int oldFloor) {
if (floor != oldFloor) {
throw new TestFailure("Elevators should "
+ "only be moved by the provided code. "
+ "Do not change 'floor'.");
}
}
private final void delay() {
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public final String toString() {
return id + "(" + (carrying ? passenger : "") + ")->"+targetFloor;
}
/**
* Should start the elevator moving in the direction of 'floor'.
* The Person that pressed the button and hailed the elevator is passed
* in as Person 'p' and should be saved in the passenger field.
*
* @param floor where the elevator should go
* @param p the person that hailed the elevator
* @throws OccupiedException if hailed but not available
*
*/
public abstract void hail(int floor, Person p) throws OccupiedException;
/**
* Construct and start the thread running this elevator.
* You must the provided 'thread' field.
*
* Note:
* We recommend you write this method first, since it will make
* the other methods easier to visualize.
*/
public abstract void start();
/**
* @return true if the elevator is available to be hailed
*/
public abstract boolean isAvailable();
/**
* Make sure to call 'p's board method.
* Elevator should start moving towards passenger's destination.
*/
protected abstract void pickUp();
/**
* @return true if the elevator should open its doors and pick up 'passenger'
*/
protected abstract boolean shouldPickUp();
/**
* Make sure to call the passenger's exit method
*/
protected abstract void offload();
/**
* @return true if the elevator can offload its passenger
*/
protected abstract boolean shouldOffload();
}
/**
* An abstract class that defines the backbone of an Elevator Controller.
* You should extend this class.
*
* The run() method, which you will implement, should wait for button presses
* and available elevators, dispatching elevators (by calling AbstractElevator's
* 'hail' method) when possible.
*
* Lock and Conditions are provided.
*
*
*/
public static abstract class AbstractElevatorController implements Runnable {
/**
* The elevators in the building.
*/
protected Set elevators;
/**
* Holds the floors whose buttons have been pressed.
*/
protected Queue floorQueue;
/**
* Holds the people who have pressed the buttons.
*/
protected Queue personQueue;
protected Thread thread;
/**
* Used to wait for button presses.
*/
protected Condition buttonPressed;
/**
* Used to wait for available elevators
*/
protected Condition elevatorFinished;
protected ReentrantLock lock;
/**
* This is private because the ElevatorController implementation shouldn't
* need to store all the buttons, the buttons should notify the controller
* independently. This field is only included for the printState method, which
* you do not need to worry about.
*/
private AbstractButton[] buttons = new AbstractButton[FLOORS];
/**
* Because this constructor does not take in any parameters, it is considered
* the default constructor, so your implementation does not need to wrap it.
*/
public AbstractElevatorController() {
floorQueue = new LinkedBlockingQueue ();
personQueue = new LinkedBlockingQueue ();
lock = new ReentrantLock();
buttonPressed = lock.newCondition();
elevatorFinished = lock.newCondition();
}
public final void setElevators(Set elevators) {
this.elevators = elevators;
}
public final void start() {
thread = new Thread(this);
for (AbstractElevator e : elevators)
e.start();
thread.start();
}
/**
* Should be called by the Buttons. It is up for the controller's
* run() method to dequeue the floor and person queues.
*
* This method is non-final, so you may override it if you like.
*
* @param floor the floor that requested the elevator
* @param p the person that requested the elevator
*/
public void request(int floor, Person p) {
floorQueue.add(floor);
personQueue.add(p);
}
/**
* Used to number the printState outputs.
*/
private int count = 0;
/**
* Prints the state of the elevator system for debugging. You do not
* need to worry about this code
*/
public final void printState() {
@SuppressWarnings("unchecked")
LinkedList[] array = new LinkedList [FLOORS];
for (int i = 0; i < FLOORS; i++)
array[i] = new LinkedList();
int longest = 0;
for (AbstractElevator e : elevators) {
array[e.getFloor()].add(e);
if (array[e.getFloor()].size() > array[longest].size())
longest = e.getFloor();
}
int size = array[longest].toString().length();
size = size < 25 ? 25 : size;
int width = String.format("| floor %2s: %7s %"+size+"s | ",
0+"",
buttons[0],
array[0]
).length();
String bottom = "+";
for (int i = 0; i < width +1; i++)
bottom += "-";
bottom += "+";
String top = "+"+count;
for (int i = 0; i < width +1 - (""+count).length(); i++)
top += "-";
top += "+";
//System.out.println(++count);
System.out.println(top);
for (int i = FLOORS-1; i >= 0; i--)
System.out.printf("| floor %2s: %7s %"+size+"s | ",
i+"",
buttons[i],
array[i]
);
System.out.println(bottom + " ");
count++;
}
/**
* Wait for pressed buttons and available elevators by calling 'await'
* on the two corresponding Condition objects. When possible, assign an
* Elevator to a waiting person by calling Elevator's 'hail' method.
*
* You should not just periodically check for pushed buttons and available
* elevators; you must use the conditions' await methods.
*/
@Override
public abstract void run();
}
/**
* This abstract class mostly defines the button that is on each floor.
* There is only a single abstract method, 'press()' which is called when
* the person on the floor with this button presses the button.
*
* You should call AbstractElevator's 'request' method.
*
*/
public static abstract class AbstractButton implements Runnable {
public final int floor;
private Thread thread;
protected AbstractElevatorController control;
private Person p;
private boolean pressed = false;
/**
* Note that when extending this class, you will have to wrap this constructor.
*
* Something like this:
*
* public Button(int floor, Provided.AbstractElevatorController control) {
* super(floor, control);
* }
*
*
* @param floor the floor the button is on
* @param control the controller for this button
*
*/
public AbstractButton(int floor, AbstractElevatorController control) {
this.floor = floor;
this.control = control;
control.buttons[floor] = this;
p = new Provided.Person(floor);
}
public final void start() {
thread = new Thread(this);
thread.start();
}
@Override
public final void run() {
try {
Thread.sleep(Provided.random.nextInt(TEST_LENGTH*1000));
} catch (InterruptedException e) {
return;
}
press(p);
pressed = true;
}
@Override
public final String toString() {
return pressed && p.elevator == null ? "waiting" : "";
}
/**
* Called when the button is pressed. Make sure to notify the controller
* by calling request()
*
* The Person parameter is constructed and passed in by the provided code
* and is used to verify that your system works, so you must make sure not
* to loose the reference to it. It should be passed to the controller through
* the request method, and it should then be passed to the Elevator through the
* hail method. This way, the Elevators can call 'board' and 'exit' on the
* provided Person objects, thereby passing the tests.
*
* @param p, the person that pressed the button.
*/
protected abstract void press(Person p);
}
// ----- The Person Class used for Testing ----- \
/**
* Class used for testing effectiveness of Elevators.
* You do not need to worry about this code. However, you do need to call
* 'board()', 'exit()', and 'getDestination()' in Elevator. You must
* also pass the Person objects from the Button 'press' method to the controller
* through the request method, and finally to the elevator through the 'hail'
* method. This is the only way to pass the tests.
*/
public static final class Person {
private int startingFloor;
private int destination;
private AbstractElevator elevator;
private boolean done = false;
public Person(int startingFloor) {
this.startingFloor = startingFloor;
this.destination = Provided.random.nextInt(FLOORS);
}
/**
* @return the floor that this person wants to go to.
*/
public int getDestination() {
return destination;
}
/**
* Call this method to make the person get on the elevator.
*
* @param e the Elevator that this Person is boarding.
*/
public void board(AbstractElevator e) {
if (elevator != null)
throw new TestFailure("Passenger " + startingFloor + " is Already "
+ "on an Elevator");
if (e.getFloor() != startingFloor)
throw new TestFailure("Cannot Pick Up Passenger " + startingFloor
+" from Floor " + e.getFloor() +".");
elevator = e;
}
/**
* Call this method to make the person get off the elevator
*/
public void exit() {
//System.out.println(startingFloor +" dropped off at " + elevator.getFloor());
if (elevator == null)
throw new TestFailure("Passenger " + startingFloor + " has not Boarded "
+ "and Cannot Exit.");
if (elevator.getFloor() != destination)
throw new TestFailure("Passenger " + startingFloor + "'s Destination is "
+destination+". He/She Cannot be Dropped Off at "
+ elevator.getFloor() +".");
done = true;
}
@Override
public String toString() {
return "" + startingFloor;
}
}
// ----- Main Method Testing ----- \
/**
* Run this to test your code.
* However, we encourage you to write your own tests as well.
*/
public static void main(String[] args) {
// ---- Initialize Test ---- //
ElevatorController c = new ElevatorController();
HashSet e = new HashSet();
for (int i = 0; i < ELEVATORS; i++)
e.add(new Elevator(c));
HashSet b = new HashSet();
for (int i = 0; i < FLOORS; i++)
b.add(new Button(i, c));
c.setElevators(e);
// ---- Start Test ---- //
c.start(); // controller starts the elevators
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if (!c.thread.isAlive()) // Test that Controller started properly
throw new TestFailure("Controller Terminated Early.");
if (!c.thread.getState().equals(Thread.State.WAITING))
throw new TestFailure("Controller Needs to Call Await on the Conditions.");
for (AbstractButton i : b)
i.start();
// ---- Sleep While Buttons are Pressing ---- //
try {
Thread.sleep(TEST_LENGTH*1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// ---- Check if Test is Complete ---- //
while (!TERMINATE) {
try {
Thread.sleep(DELAY*4); // check every fourth cycle
} catch (InterruptedException e1) {
e1.printStackTrace();
}
boolean stillGoing = false;
for (AbstractElevator i : e)
if (i.isMoving()) // if an elevator is moving, stop
stillGoing = true;
if (!stillGoing) {
for (AbstractButton i: b)
if (!i.p.done) // if stopping, everyone should be delivered
throw new TestFailure("Did Not Drop Everyone Off!");
if (!c.thread.getState().equals(Thread.State.WAITING))
throw new TestFailure("Test Ended with Controller in "
+ "Non-Awaiting State");
TERMINATE = true; // Tell all threads to stop
c.thread.interrupt();
}
}
if (passed)
System.out.println("All tests passed!");
}
}
Explanation / Answer
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;
public class MyLift {
public static void main(String[] args) {
System.out.println("Welcome to MyLift");
// RequestListenerThread to read requested floor and add to Set
Thread requestListenerThread = new Thread(new RequestListener(),
"RequestListenerThread");
// RequestProcessorThread to read Set and process requested floor
Thread requestProcessorThread = new Thread(new RequestProcessor(),
"RequestProcessorThread");
Elevator.getInstance().setRequestProcessorThread(requestProcessorThread);
requestListenerThread.start();
requestProcessorThread.start();
}
}
class Elevator {
private static Elevator elevator = null;
private TreeSet requestSet = new TreeSet();
private int currentFloor = 0;
private Direction direction = Direction.UP;
private Elevator() {};
private Thread requestProcessorThread;
/**
* @return singleton instance
*/
static Elevator getInstance() {
if (elevator == null) {
elevator = new Elevator();
}
return elevator;
}
/**
* Add request to Set
*
* @param floor
*/
public synchronized void addFloor(int f) {
requestSet.add(f);
if(requestProcessorThread.getState() == Thread.State.WAITING){
// Notify processor thread that a new request has come if it is waiting
notify();
}else{
// Interrupt Processor thread to check if new request should be processed before current request or not.
requestProcessorThread.interrupt();
}
}
/**
* @return next request to process based on elevator current floor and direction
*/
public synchronized int nextFloor() {
Integer floor = null;
if (direction == Direction.UP) {
if (requestSet.ceiling(currentFloor) != null) {
floor = requestSet.ceiling(currentFloor);
} else {
floor = requestSet.floor(currentFloor);
}
} else {
if (requestSet.floor(currentFloor) != null) {
floor = requestSet.floor(currentFloor);
} else {
floor = requestSet.ceiling(currentFloor);
}
}
if (floor == null) {
try {
System.out.println("Waiting at Floor :" + getCurrentFloor());
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// Remove the request from Set as it is the request in Progress.
requestSet.remove(floor);
}
return (floor == null) ? -1 : floor;
}
public int getCurrentFloor() {
return currentFloor;
}
/**
* Set current floor and direction based on requested floor
*
* @param currentFloor
* @throws InterruptedException
*/
public void setCurrentFloor(int currentFloor) throws InterruptedException {
if (this.currentFloor > currentFloor) {
setDirection(Direction.DOWN);
} else {
setDirection(Direction.UP);
}
this.currentFloor = currentFloor;
System.out.println("Floor : " + currentFloor);
Thread.sleep(3000);
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public Thread getRequestProcessorThread() {
return requestProcessorThread;
}
public void setRequestProcessorThread(Thread requestProcessorThread) {
this.requestProcessorThread = requestProcessorThread;
}
public TreeSet getRequestSet() {
return requestSet;
}
public void setRequestSet(TreeSet requestSet) {
this.requestSet = requestSet;
}
}
class RequestProcessor implements Runnable {
@Override
public void run() {
while (true) {
Elevator elevator = Elevator.getInstance();
int floor = elevator.nextFloor();
int currentFloor = elevator.getCurrentFloor();
try{
if (floor >= 0) {
if (currentFloor > floor) {
while (currentFloor > floor) {
elevator.setCurrentFloor(--currentFloor);
}
} else {
while (currentFloor < floor) {
elevator.setCurrentFloor(++currentFloor);
}
}
System.out.println("Welcome to Floor : " + elevator.getCurrentFloor());
}
}catch(InterruptedException e){
// If a new request has interrupted a current request processing then check -
// -if the current request is already processed
// -otherwise add it back in request Set
if(elevator.getCurrentFloor() != floor){
elevator.getRequestSet().add(floor);
}
}
}
}
}
class RequestListener implements Runnable {
@Override
public void run() {
while (true) {
String floorNumberStr = null;
try {
// Read input from console
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
floorNumberStr = bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (isValidFloorNumber(floorNumberStr)) {
System.out.println("User Pressed : " + floorNumberStr);
Elevator elevator = Elevator.getInstance();
elevator.addFloor(Integer.parseInt(floorNumberStr));
} else {
System.out.println("Floor Request Invalid : " + floorNumberStr);
}
}
}
/**
* This method is used to define maximum floors this elevator can process.
* @param s - requested floor
* @return true if requested floor is integer and upto two digits. (max floor = 99)
*/
private boolean isValidFloorNumber(String s) {
return (s != null) && s.matches("\d{1,2}");
}
}
enum Direction {
UP, DOWN
}
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.