views:

148

answers:

3

I'm writing a simulation of Bananagrams for fun. I want to use concurrency but I'm not entirely sure how.

I have a main method in a Game class. Each of the player threads works towards a solution. At certain points, a player will "peel". During this operation, every player is dealt a new tile. One of the player threads must notify the Game thread.

The pseudocode would look something like this:

while (no player has reported they are finished) {
      if (player reports it is time to peel) {
           everyone peel 
      }
      everyone work towards completion of puzzle
}

How can I implement this in Java? (I'm not necessarily looking for fully fleshed out solutions, just point me in the right direction.) How do I want to handle notification between objects?

To clarify: this is not a user-interaction game. I just want to try different algorithms to see which can solve the problem the fastest. If anything, the "game" would be writing an algorithm and plugging it in to see how it works.

+1  A: 

The Observer pattern might be appropriate. Everyone registers with the main thread to receive notifications on peel events. When one person reports that its time to peel, he notifies the main thread, who in turn notifies all registered threads. Notification could be done through a special thread-local variable (each player thread has his own) that is set by the main thread via a method call and checked by the player thread at each iteration of the game loop.

Edit: here's a link to an article that goes more into depth regarding implementing the Observer pattern in multithreaded fashion in Java. http://www.javaworld.com/jw-03-1999/jw-03-toolbox.html

danben
+3  A: 

This would be a good place to use a Cyclic Barrier.

Essentially a Cyclic Barrier lets threads do some work, then have all the threads wait until they all get to the same point, then each thread starts again.

So, you could have each player peel, then call CyclicBarrier.await(). All the threads will then wait until each has reached that point. Which seems to be what you want.

(Also, you obviously don't really need concurrency for this :)

Chad Okere
A: 

Depending on exactly what your process is, you might not need to do threading (Which, trust me, is something that you'd rather avoid if at all possible, no matter how cool and fun the big kids make it out to be).

One way to approach the problem is to set up an event queue.

in pseudocode

 enum EVENTTYPES = {PEEL=0, WORK=1};
 struct Event = {
     int eventType;
     int* data;
 }

 filoQueue eventQueue;

 array sQuidPlayers = [new Squid(), new Squid(), new Squid()];
 void eventLoop () {
      int player;
      for each player in sQuidPlayers {
          eventQueue.push(new Event(EVENTTYPES.WORK, player.id));
      }

      for each event in eventQueue {
           game.doEvent(event)
      }

 }

So here you run the event loop 25 times, 30 times, or 60 times per second, whatever frame rate you want to operate at. You use a timer for that (i'm sure there's one in java somewhere)

Then doEvent will try and look up some cooresponding method on the cooresponding player instance. The work method on the Squid class will do some tiny parcel of work, and then stop, waiting for the next time around in the loop. Each Squid in the array gets their turn to do their tiny bit of work. The work method, in turn, MAY put a PEEL event onto the event queue. At that point, next time around the loop, some cooresponding peel method may be called. Perhaps on some central game class, with the id of the player that originated the peel event. You put the logic of how to dispatch these events into the doEvent method there. doEvent in turn can pass in an object to each event reciever, so that the reciever can put their own event objects onto the queue to be run next time around the loop. (alternately, the "for each event" loop runs until the eventqueue is empty, and that includes new events just added by previous calls to doEvent, so a new event can get called immediately instead of waiting for the next frame of animation)

The trick is figuring out how to break your long running work into a tiny parcel of work, figure out how to save the results of that work, and pick it up later where you left off the next time the work method is called. If all the players are well behaved, they can all share a thread without getting stuck.

If you're going to go down the threaded path, the issues about who can access what bit of memory and when get a little bit more complicated.

Breton