Jump to content
  • Advertisement
Sign in to follow this  
kloffy

Thread Synchronization (Java)

This topic is 4056 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I can't figure out how to properly synchronize threads in the following scenario:
public class StateMachine implements Runnable {
	public enum State {
		INTRO {
			public State execute(final StateMachine game) {
				return IDLE;
			}
		},
		GAME {
			public State execute(final StateMachine game) {
				return GAME;
			}
		},
		IDLE {
			public State execute(final StateMachine game) {
				return IDLE;
			}
			
		},
		END {
			public State execute(final StateMachine game) {
				return null;
			}
		};
		
		public abstract State execute(final StateMachine game);
	}

	private State state;
	
	public StateMachine() {
		this.state = State.INTRO;
	}
	
	public void setState(State state) {
		this.state = state;
	}
	
	public void run() {
		while(state!=null){
			setState(state.execute(this));
		}
	}
	
	public static void main(String[] args) {
		new StateMachine().run();
	}
}
The problem arises when the user triggers a state change from another thread (Swing). Lets say the main thread is in the execute method of IDLE when it gets interrupted by the GUI thread. The user chose to start a new game, so setState(State.GAME) is called asynchronously. Now, the main thread continues with the execution of IDLE and returns State.IDLE. State.GAME is overridden and the state machine is back in an IDLE state. The users action is ignored.

Share this post


Link to post
Share on other sites
Advertisement
Basically the problem comes down to making sure setState(state.execute(this)) doesn't get interrupted. If a state change is requested by the user, it needs to happen before or after setState(state.execute(this)). One naive attempt for a solution would be to make StateMachine.setSate synchronized. That doesn't work though, since error occurs when the main thread is interrupted during State.execute.

Share this post


Link to post
Share on other sites
Quote:
Original post by kloffy
Basically the problem comes down to making sure setState(state.execute(this)) doesn't get interrupted. If a state change is requested by the user, it needs to happen before or after setState(state.execute(this)). One naive attempt for a solution would be to make StateMachine.setSate synchronized. That doesn't work though, since error occurs when the main thread is interrupted during State.execute.


Well, there are actually many more problems, mostly that the design doesn't make any sense.

Why is StateMachine a Runnable? It doesn't do anything by itself.

Why is the enum designed the way it is, when it performs exactly the same role as the class it's contained in?

Why does it accet StateMachine when it's called only with this?

Why aren't you propagating the changes between states via observer pattern, or using vetoable listeners?

Yes, the proper solution for synchronization is to make things synchronized on a common lock. If you have any other issues with this, or if this isn't adequate, then you'll need ensure transactional safety, or change the way it's used.

Your state machine is just that:

public class StateMachine
{
private State state; // = whatever, class enum, int

List<StateListener> listeners = new ArrayList<StateListener>();

public synchronized bool setState( State newState )
{
State oldState = state;
state = newState;
for (StateListener l : listeners ) {
if (!l.stateChanged( this, oldState, newState )) {
state = oldState;
return;
}
}
}

public void addListener( StateLsitener l ) { listeners.add(l); }
public void removeListener( StateListener l ) { listeners.remove(l); }
}

interface StateListener
{
public bool stateChanged( StateMachine, State oldState, State newState );
}




So...

When you call setState, all of registered listeners are notified. setState will return true if all listeners agree with state change, or false if at least one of them rejected the state transition.

If any of the listeners rejects the transition, then the old state is restored.

You then simply implement listeners for various events. For example, you application might only be interested when the user exists the game, and would only care about such events.

Your "Level" object would listen for "End of Level", "Player Died", "Player Quit" events. And so on....

Of course, your original code might not be what you really have, so a different design would be better suited.


The core of your design problem comes from making StateMachine constantly running (while (...)), whereas StateMachine, by very definition is event driven (it's idle, until something happens, upon which it responds).

Alternatively, you could make StateMachine passive, and check and handle its state in a single place, thereby bypassing the threading issues altogether.

Share this post


Link to post
Share on other sites
Thanks for your suggestions. Obviously the code I provided was just an example, the real code is different. In my real application every state performs some calculation during State.execute, except for the IDLE state. The reason State.execute gets StateMachine as a parameter is to give the states a way to manipulate StateMachine data. Making the StateMachine passive could be an option, I'll have a look into that and see how it plays out.

Share this post


Link to post
Share on other sites
I agree with the recommendations Antheus makes, but they stop at taming state updates.
There is a more fundamental problem: accessing the state from outside (public setState() method) is utter nonsense, because the state machine is the sole responsible of its state and must change it of its own accord only (in this case in the run() method, which is a bad idea for different reasons).

Nothing outside the class in question should know there is a state machine, not to mention what states it has; any meaningful class has methods that might represent commands or even imply state changes (e.g. pauseGame() or resumeGame() for a main loop manager), but the fact that one of the things an object does when they are called is switching a state variable (e.g. between GAME and PAUSE) is a private and inconsequential implementation detail.

Share this post


Link to post
Share on other sites
I cannot quite imagine how to implement this. I would appreciate I you could post a simple example (with a main loop and some state changes). It doesn't need to do anything, just to give me an idea how it would look in code. Thanks in advance!

Edit: Here's a design that's pretty similar to what I tried to do. Maybe his example is better.

[Edited by - kloffy on November 2, 2007 10:44:05 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!