This question is based on material in Unit 8. A NetBeans project TMA03Q2 is provided

The Ferry Boat Problem

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:

  • EASTBOUND means that the ferry is either at the west landing stage, ready to cross eastwards (Figure 1) or actually doing so (Figure 2)
  • WESTBOUND means the ferry is either at the east landing stage, ready to cross westwards or actually doing so.

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:

  • Delays. This class exists only to provide two static utility methods used by other classes in the project:
    • delay(int ms), which introduces a sleep of duration ms milliseconds
    • randomDelay(int ms), which introduces a sleep of random duration between 0 and ms milliseconds. The classes that use these methods contain what is known as a static
    • import: import static utilities.Delays.*; The static import allows the methods to be called from within those classes just by using the simple name of a method, e.g. delay(2000); rather than the qualified name utilities.Delays.delay(2000);. This helps to keep the code clear and uncluttered.
  • Direction. This is an enum, a special kind of class that you will not have met in the module text but which is quite easy and intuitive to understand. An enum simply defines a new Java type that can only take one of a fixed range of values listed in the enum definition. For example we could define an enum named Season with values SPRING, SUMMER, AUTUMN and WINTER. In our case Direction defines just two values, EASTBOUND and WESTBOUND, which represent the two possible directions of the ferry and passengers. The classes that use Direction have a static import import static utilities.Direction.*; which allows us to refer to the directions using just the simple names EASTBOUND and WESTBOUND.

In package tma03q2:

  • Ferry. An instance of this class represents the ferry. Ferry has an instance variable Direction direction with accessor methods getDirection() and setDirection(Direction). There is also a helper method swapDirection() that switches the direction, from EASTBOUND to WESTBOUND or vice versa. A second instance variable List passengerList stores references to the passengers currently aboard. This class has methods board(Passenger), which will be invoked by a passenger thread, and cross(), which will be invoked by the conductor thread. There is no method that passenger threads invoke to disembark; instead the cross() method clears the passenger list at the end of its execution.
  • FerryConductor. An instance of this class represents the ferry conductor. A FerryConductor has a Ferry that represents the ferry the conductor is responsible for. The class FerryConductor extends Thread and has a run() method that uses a for loop that repeatedly delays for 2 seconds, then invokes cross() on its ferry. The cross() method currently performs 10 ferry crossings, five in each direction. We estimate this will be sufficient for testing purposes but you can increase the number if necessary.
  • Main. The main() method in this class first creates an instance of Ferry and an associated FerryConductor, and sets the latter running. It then creates six passengers, bound alternately east and west, starting each one as it is created. To simulate the random arrival of passengers that would be observed in real life a random delay of up to 2000 milliseconds is introduced in between the creation of each Passenger thread. You may wish to adjust this delay to test your code further.
  • Passenger. An instance of this class represents a passenger. Passenger has three instance variables:
    • Direction direction, representing the direction in which the passenger is travelling
    • int idNum, representing a passenger’s identity
    • Ferry ferry, which represents the ferry.

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?

  • One suggestion would be to have a policy that the ferry must never set off empty, so if cross() is invoked and the passenger list is empty the conductor thread is made to wait. Explain in one sentence why this simple policy would not be satisfactory (at least, not in real life). We are not looking for anything particularly deep or technical: simply ask yourself what the result of such a policy could be.
  • Make changes to the Ferry class so that when cross() is invoked the ferry conductor thread will be made to wait unless one of the following conditions apply:
    • at least one passenger is on board
    • at least one passenger has invoked board(Passenger) but has been made to wait.

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.

Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.