InterferenceTopAnnouncementsConcurrency

Concurrency

We introduced active objects early in the course as a way of providing objects which behaved independently of user actions. Examples include vehicles and lanes from Frogger, the ball in BoxBall, and the falling snow class example. Our ActiveObject class encompasses what are usually termed threads of control. The Frogger game, for instance, included separate threads for each vehicle, each lane, and the part of the system that listened for user clicks and moved the frog appropriately.

Today, we will discuss complications that arise with threads and provide some indications of how to handle them. As a first example we look at a program, BetterBubble, which uses a graphic user interface in order to simulate a bubble pipe. When the user pushes the "start blowing" button, a bubble starts growing at the top of the bowl of the pipe. When the "release blowing" button is pushed, the bubble stops blowing and floats off of the top of the screen.  
BetterBubble demo 
If we did not use an ActiveObject (or Thread) for this program, it would not work properly. That is because the process that handles the button presses is also responsible for drawing the pictures on the screen. If that process is busy moving the bubble it will not have the opportunity to listen to button presses or to redraw pictures on the screen.

The standard way to ensure prompt processing of user interface events is to make sure that the thread handling user events is never tied up with long, complex actions. Instead, we create a new ActiveObject (or Thread) to handle the complex task, so that the user thread can stay alert to new events.

The begin() method draws the bubble pipe and creates and installs the buttons. When the start button is pressed, the constructor of BubbleBlower is called. This saves the canvas in an instance variable and starts the new thread. The event-handling thread then returns, waiting for another user event.

When the release button is pressed, it sends the stopBlowingBubble method to blower. Thus the event-handling thread sets the stopBlowing variable of blower to be true. It then returns and waits for another user action.

By the way, notice that we have labelled the stopBlowing instance variable to be volatile because if is read and changed by two separate threads. Because of the complexities of memory in modern computers (most everything you've heard about memory is a great simplification of reality), you must let the system know about variables that are touched by more than one thread. One way of doing that is to declare shared variables to be volatile. Another way is to protect them with synchronized (see below).

Meanwhile, the blower's own thread is busy executing the while loop in the run method. However, when it now finishes execution of the body of the while loop, it will notice that the value of stopBlowing has changed, and it will exit the loop. Thus the execution of the while loop will terminate. However the next statement after the while loop is the method releaseBubble. Thus it will start floating the bubble. When the bubble has floated off the screen, the releaseBubble and run methods will terminate and the active object will die.

Each press of the start button will result in the initiation of a new active object to handle the blowing and release of a new bubble. Note however, that all is not entirely well. If the user presses start twice in a row without hitting release between the two, the first bubble can no longer be affected by clicking on release. See if you can figure out why this happens, and how we might fix it in some way.


InterferenceTopAnnouncementsConcurrency