The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search

Trail: Putting It All Together
Lesson: BINGO!

Coordinating Threads in the Game

The RingMaster(in a .java source file) class provides two methods that the GamesThread uses to coordinate its activities with the other threads in the Game application: waitForFirstPlayer and waitForGameToEnd. As the names of these methods imply, these methods cause GamesThread to wait until for a condition set by another thread.

Waiting for the First Player to Register

The run method for GamesThread contains a loop that continues until told to stop by the ControlPane. Each iteration through the loop is a single BINGO game. At the top of the loop, and therefore at the beginning of each BINGO game, the run method for GamesThread calls RingMaster's waitForFirstPlayer method which looks like this:
synchronized void waitForFirstPlayer() {
    gameNumber++;
    state = WAITING;
    socketGate.sendGameStatusMessage(statusString());

    while (state == WAITING) {
        try {
           wait();
        } catch (InterruptedException e) {
        }
    }

    socketGate.sendGameStatusMessage("Beginning count down ... ");
}
In plain English, this method causes the GamesThread to wait until the first Player registers. Let's look at the code to find out how.

First the method sets the RingMaster's state to WAITING. Then, waitForFirstPlayer enters a loop that causes the current thread to wait as long as state is still WAITING. Which begs the question "How can this loop ever end?" The answer is that another thread, a thread in the Player application changes state and wakes up the GamesThread. Here's how.

To register, a Player application makes a remote method call to RegistrarImpl's mayIPlay method. Part of the registration process involves adding a PlayerRecord to the Roster which, if this is the first player to register, also involves calling the RingMaster's startCountDown method. startCountDown looks like this:

synchronized void startCountDown() {
    state = COUNTINGDOWN;
    notifyAll();
}
This method changes the state to COUNTINGDOWN and then calls notifyAll. notifyAll causes GamesThread to return from its wait method. Remember that the call to wait is in a loop that continues as long as state is WAITING. So after GamesThread returns from wait, it checks the state, which is no longer WAITING and so exits the loop. So this method causes GamesThread to return from waitForFirstPlayer.

Thus when GamesThread calls waitForFirstPlayer the effect is that the threads stops and waits until the first player registers for the game. Then the count down begins.

[PENDING: consider diagraming the flow of this using diagrams similar to Doug Lea's]

Waiting for the Current Game to End

GamesThread uses a very similar mechanism to wait until the current game is over. A game ends when a Player wins or the BallAnnouncer runs out of balls). The code is very similar to that used by GamesThread to wait for the first player to register so we'll briefly point out the classes and methods involved and let you figure out the rest.

After GamesThread has created a BallAnnouncer and started it, GamesThread calls RingMaster's waitForGameToEnd method which looks like this:

synchronized void waitForGameToEnd() {
    while (gameInProgress()) {
        try {
           wait();
        } catch (InterruptedException e) {
        }
    }
}
Like the waitForFirstPlayer method, this method waits until the state changes and indicates that the game is no longer in progress. This causes the GamesThread to go into a wait state and do nothing.

The state gets changed when RingMaster's setGameOver method gets called.

synchronized void setGameOver() {
    state = GAMEOVER;
    announceBall(new BingoBall(BingoBall.GAME_OVER));
    announcedBalls.removeAllElements();
    announcedBalls.push(new BingoBall(BingoBall.FREE_SPACE));
    roster.removeAllElements();
    notifyAll();
}
setGameOver can be called under two conditions:
  1. A Player won the game. When a Player wins, it remotely calls RegistrarImpl's BINGO method which verifies the claim. If the BINGO claim is valid, then BINGO calls setGameOver.
  2. The BallAnnouncer announced all of the balls and there was no winning claim from any Player. In this case the BallAnnouncer calls setGameOver.
setGameOver changes the RingMaster's state and then calls notifyAll which wakes up GamesThread.

Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search