This question is based on material in Unit 8. A NetBeans project TMA03Q2 is provided
In this question you will develop a simple simulation of a ferry that shuttles back and forth across a stretch of water. At a given moment the ferry is in one of two states: EASTBOUND or WESTBOUND:
See figure: See image.
The users of the ferry are simulated by Passenger threads, who may board while the ferry is paused at a landing stage (but not of course while it is crossing). To simulate boarding, they invoke the board(Passenger) method on the Ferry object. When a passenger invokes this method, it passes itself as argument so the ferry can add them to a passenger list.
Passengers may be EASTBOUND or WESTBOUND travellers. EASTBOUND travellers arrive at the west landing stage and may not board unless the state of the ferry is EASTBOUND and the ferry is not yet crossing. Similarly, WESTBOUND travellers arrive at the east landing stage and may not board unless the ferry is WESTBOUND and is not yet crossing (Figure 3). See image.
The passage of the ferry across the water is simulated by a FerryConductor thread, which invokes a cross() method on the Ferry object.
In the simulation, time is speeded up considerably compared with real life so our experiments will not take too long to run.
The project we have supplied is an incomplete prototype. It will compile and run but doesn’t fully implement all the conditions described above. You are asked to complete the coding, so that all the conditions are met, and then to make some further changes to improve the realism of the simulation.
Open the project TMA03Q2 and expand it fully in the Projects window. It contains the following classes. Note that the only one of these you will be asked to change is Ferry.
In package utilities:
In package tma03q2:
None of these variables has a setter, since they should not change once the Passenger has been created. However, there are getters for the direction and identity number, and a three-argument constructor.
Passenger extends Thread and has a run() method that reports the passenger’s arrival at the landing stage and then invokes the board(Passenger) method on the ferry object.
Begin by opening each of the classes described above and reading quickly through them to get a general idea of how they work, but without going into too much detail at this stage.
Now run the project. You should find the simulation is fatally flawed. The passenger threads can board the ferry without regard to whether the ferry is at the correct landing stage, and even while it is in mid- crossing. Obviously, this violates the real-life conditions we are trying to model.
Copy a suitable extract from the output into your TMA 03 Solution Document and explain briefly how it demonstrates that the existing program allows passengers to board when it should be impossible. (If the output does not demonstrate what it should, try shortening the random delay between the creation of passengers in the main() method.)
The first step towards fixing these deficiencies is to make it impossible for a passenger thread to execute board(Passenger) at the same time as cross() is being executed by the ferry conductor thread. Make the required changes to the code of the board() and cross() methods in class Ferry. Note that all you need do is insert an extra keyword in the header of each method.
In your TMA 03 Solution Document include the two lines that you changed and an extract from the output when you run the project again, demonstrating that now travellers don’t board while the ferry is crossing. (Passengers will still be able to board from the wrong bank though – that problem isn’t fixed yet.)
Further modifications are needed to enforce the requirement that a passenger cannot board if the ferry is not at the appropriate landing stage.
Passenger and Ferry both have a getDirection() method. Therefore it is possible for the board(Passenger) method to obtain the direction of the passenger and the current direction of the ferry, and compare the two. If they are not the same, the passenger thread should be made to wait.
Once a passenger thread is waiting, there has to be some means of waking it up again when the condition of the ferry changes and it becomes permissible for the passenger to board. The direction of the ferry is changed at the end of the cross() method, and at that point a line of code must be added that will wake up any waiting threads. Make the necessary amendments to the code. Copy the modified board(Passenger) and cross() methods into your Solution Document, highlighting the changes you have made.
A real ferry would only be permitted to carry up to a certain maximum number of passengers per trip, for safety reasons. Change the code again so that at most two passengers can board per trip. (Obviously, the maximum might be considerably greater than this in real life but we need to keep the numbers small in the simulation so it doesn’t get unwieldy.)
Make the required alterations and copy the code you have changed into your Solution Document.
In the main() method, shorten the random delay to 500 milliseconds or less (this is so that enough travellers bound the same way arrive together to test the restriction to two passengers per crossing) then run the program again. Copy enough of the output into your Solution Document to show that the simulation is working correctly, that is passengers can board only from the correct bank and no more than two are carried per ferry trip. Indicate where in the output it is possible to observe that only two passengers were allowed to board at once.
In this part we consider a possible extension to the Ferry Boat Problem.
In the existing model the passengers synchronize with the ferry conductor, in order to board only when appropriate. However, the ferry conductor does not synchronize with the passengers but simply maintains a fixed schedule, thus potentially making many pointless trips. How might the conductor be made to operate more efficiently?
If your solution is correct you should find when you run the program that all six travellers cross in some order, then the ferry waits indefinitely. To stop the program, select Stop Build/Run from the NetBeans Run menu or click on the cross to the right of the running task display in the bottom of the NetBeans window. You may need to stop more than one project running if you ran your program several times.
You might suppose that the only way a thread that has been suspended by wait() can be woken is by some other thread invoking notify() or notifyAll(). Perhaps surprisingly, it turns out that spurious wakeups may be possible, in which a thread is awakened even though there has been no notification. (This can happen in other languages, not just Java.)
However, if the section of code where the call to wait() occurs has been written correctly, spurious wakeups present no problem. Explain briefly why not.