4

I have written a Java ME puzzle game. I have written the code thus: there is a thread that starts when the app starts, and, once the game has got going, there's a second thread that just runs in an infinite loop -- the main game loop. The second thread looked like this, at one point:

public void run() {
    init();
    while (shouldIRun) {            
        updateGameState();
        checkUserInput();
        updateGameScreen(getGraphics());
        this.flushGraphics();         
    }
}

Lovely. This thread just runs and runs, until I want to kill it, when I just set the boolean shouldIRun to false, whereupon it exits gracefully.

But later on I realised I wanted more. The game is a puzzle game and it's possible for the player to make the wrong moves and then get stuck. When this happens they can fire up a form and select the "restart level" option. A flag restartLevel then gets set, and when the infinite loop gets to the updateGameState() method the level is restarted. But this feels to me like a bit of a gamble -- I don't want to start changing variables of objects being used in the main loop in case of concurrency issues, although I'm probably being paranoid. In practice what I realised I wanted to do was very clear: I simply wanted to pause the infinite loop thread, change the variables to what I wanted, and then restart.

I have done this in the following way:

public void run() {
    init();
    while (shouldIRun) {            

        if (shouldIWait) {
            iAmWaiting=true;
            while (shouldIWait) { };
            iAmWaiting=false;
        }

        updateGameState();
        checkUserInput();
        updateGameScreen(getGraphics());
        this.flushGraphics();
    }
}

What I am thinking is the following. If I now want to "pause" this second thread, from the "base" thread, I just set the shouldIWait variable to true, and then just loop until I notice the iAmWaiting variable is also true. I now know for sure that the second thread has paused and I know precisely where it has paused, where by "paused" I actually mean "is stuck in an infinite loop for the time being". I can now goof around with some essential variables, restart the level, and generally sort things out, and then finally set shouldIWait back to false and off we go again.

My question is this: this works fine, for me, but smacks of being a kludge. Is there some completely standard way of doing what is presumably a common thing -- pausing a thread at a given point and then restarting it when I'm ready, which is better than what I'm doing? In particular I suspect that "putting java into an infinite loop" is perhaps not a clever thing to do.

gnat
  • 6,213
  • 108
  • 53
  • 73
Kevin Buzzard
  • 537
  • 4
  • 11
  • You could at least introduce a short thread sleep into while(shouldIWait) – Ben Flynn Feb 03 '12 at 23:25
  • I see -- to force the second thread to sleep and hence to prod the main thread back into action? OK -- done :-) Is it still a kludge though? I was expecting someone to say "oh just use t.pauseAtThisPointUntilISaySo()" or something... – Kevin Buzzard Feb 03 '12 at 23:31
  • Yeah, that's why I didn't offer it as an answer. =) I'm guessing someone better at it with me will chime in with a discussion of thread waiting, but I think what I suggested is some kind of spinlock and there are ways to use yielding and interrupts that could also be effective. – Ben Flynn Feb 03 '12 at 23:40
  • I don't think it's super kludgey, but I could be wrong. – Ben Flynn Feb 03 '12 at 23:42
  • 1
    I hope shouldIWait is volatile. – toto2 Feb 04 '12 at 00:34
  • "graciously" ? perhaps you wanted "gracefully" instead? – JustJeff Feb 04 '12 at 00:35

2 Answers2

1

Normally, this is what you would use Object.wait() and Object.notify() for.

There are a couple of ways to implement it for your situation, but here's a simple example:

Object monitor = new Object();
volatile boolean done = false, wait = false;

/* Running on one thread: */
public void run() {
    synchronized(monitor) {
        while(!done) {
            while(wait) {
                monitor.wait();
            }
            gameLogicAndStuff();
        }
    }
}

/* Running on another thread: */
public void showResetForm() {
    wait = true;
    synchronized(monitor) {
        actuallyShowResetForm();
        wait = false;
        monitor.notifyAll();
    }
}
Dolda2000
  • 25,216
  • 4
  • 51
  • 92
  • I'll accept this because it taught me something. Whether or not I'll get round to implementing it is another matter :-) but I probably will, at the end of the day. – Kevin Buzzard Feb 04 '12 at 12:36
1

Maybe it would just be simpler to kill the thread and start a new one with the new level.

If there is some information that needs to be carried from one level to the next, maybe you could refactor your code such that you gather some general information first and then start a thread for each level. (And by starting a thread, I mean using a thread pool.)

I don't think what you are currently doing with busy waiting is evil. As Ben Flynn mentioned in the comments, you could make it semi-busy waiting by looping over Thread.sleep(50).

toto2
  • 5,306
  • 21
  • 24
  • A previous version of the program used to do this, but various parameters needed to be passed and I was a little worried about certain reference loops which looked like they might occur -- and then I realised I could just do what I suggested above and went with that instead. – Kevin Buzzard Feb 04 '12 at 12:35