Session 22

Listeners and Threads in the Pinball Game

CS 2530
Intermediate Computing

Understanding the Pinball Game Kit

an example of the pinball game kit in action

I ask...

And you say...

What questions do you have about the pinball game kit?

You may be suffering some separation anxiety. Decentralizing control can cause a sense of fragmentation to programmers who are used to writing an omniscient, omnipotent main() procedures. This will largely go away as you gain OOP and Java experience.

Threads of Execution

Our BallWorlds and our CannonGame combine the animation (controlling the movement and location of the balls) with the interface (the painting of the balls). Logically, these tasks are unrelated. We know this means that they should be in different parts of our program: different methods, or even different objects:

The pinball game separates these responsibilities into different objects. This separation simplifies all of the methods involved, which makes it easier to write them, read them, and modify them. The cost is an increase in the number of methods needed and classes needed.

A thread is way to represent an individual task. Two different threads can be executing in the same program, acting independently when it comes their their particular tasks and occasionally interacting with the rest of the program, even each other.

Java provides a Thread class that enables us to define threads specific to our program. This class provides methods to create, start, run, sleep, and stop an independent path of computation in the program. This allows us to implement logically distinct tasks that run at the same time, from the perspective of the user.

In our pinball game, we use a unique thread for each ball that the user launches. The thread will control the ball's movement and manage its interactions with the other objects. It tells its ball to move and then queries the targets in the game to see if the ball has hit them.

A Thread object knows how to manage the overhead of a separate task for us. Once we send a Thread a start() message, we can simply watch it go! This is similar to the benefit we get from using a Frame in the AWT, or any object, really: the object knows how to take care of itself, so we don't have to take care of it ourselves.

Each thread runs independently. Different threads may be interacting with different parts of the program. For instance the thread for one ball may be moving, while another may be talking to a target to see if the ball has hit it.

However, two threads may be interacting with the same part of the program at the same time. Consider the score:

    private int score;

This variable tracks the number of points the user earns for hitting targets in the field of play. When a pinball strikes a target, the target tells the pinball game to add its point total to the instance variable, by sending an addScore message. Here is the addScore method:

    public void addScore( int value )
      score = score + value;
      scoreLabel.setText( "score = " + score );

Consider this simple scenario:

two pinballs about to hit the same scorepad

What if the two balls hit the scorepad "at the same time"? Each will send an addScore message to the frame, at about the same time.

Having separate threads of control creates a new kind of problem. In order to give the illusion that independent tasks are happening at the same time, the JVM occasionally interrupts one task to service another. It then returns to complete the first task later.

What happens if the addScore method is interrupted? As long as it completes its first statement, everything will be okay. But each Java statement requires several instructions at the machine level. The statement that updates the score requires at least four different operations:

  1. Retrieve the value of score.
  2. Retrieve the value of value.
  3. Compute the sum of these two.
  4. Store the result in score.

The JVM can interrupt addScore() at any point in this process. If it interrupts the first ball after Step 1... [Show an example.]

To prevent this from happening, we need to instruct the JVM not to interrupt addScore() at all. This is the purpose of synchronized, the new access modifier you see here. synchronized acts like a "do not disturb" sign to other threads and the JVM. Once any piece of code begins to execute addScore(), the presence of synchronized guarantees that no other piece of code can begin to execute it until the first has finished. Each thread may be independent, but it has to get in line to use the addScore() method.

A Little Fun...

Take a look at this funny CatchEugene, a variation of your game of Catch. It has a couple of interesting features.

First, and most fun, it lets you hurl my crazy smiling head across the canvas. "Pousse this, pal!"

Second, and most immediately practical, it shows you how to use and draw an image in a frame, panel, or canvas. Feel free to use this feature in your own code.

Third, and most important from a software design perspective, it makes us duplicate code in a new way. I need a different kind of ball than in my earlier implementation, so I have to copy the class and replace the code that creates the ball with new code. Everything else stays the same.

This is a "new" kind of problem -- with new! How can we make this duplication go away?

Wrap Up

Eugene Wallingford ..... ..... November 1, 2012