Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (476)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (532)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Actor Model : Java short implementation  (Read 2663 times)
0 Members and 1 Guest are viewing this topic.
Offline Gudradain
« Posted 2012-08-15 23:58:02 »

I just learned about the actor model a while ago and decided to see if I could do something similar in Java. The result is pretty short, only 2 class and it works.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public class Message {
   
   public final Object object;
   public final Actor source;
   
   public Message(Object object, Actor source){
      this.object = object;
      this.source = source;
   }

}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
import java.util.ArrayList;
import java.util.HashMap;

public abstract class Actor implements Runnable{
   
   private Thread thread;
   private ArrayList<Message> messageList = new ArrayList<Message>();
   private HashMap<Object, ArrayList<Message>> waitMap = new HashMap<Object, ArrayList<Message>>();
   private Object current = null;
   
   private synchronized void add(Message m){
      messageList.add(m);
      notify();
   }
   
   private synchronized Message get(){
      return messageList.remove(0);
   }
   
   private synchronized void waiting(){
      if(messageList.size() == 0){
         try { wait(); }
         catch (InterruptedException e) { } //Do nothing
     }
   }
   
   public void wait(Object key, Message m){
      ArrayList<Message> list = waitMap.get(key);
      if(list == null){
         list = new ArrayList<Message>();
         waitMap.put(key, list);
      }
      list.add(m);
   }
   
   public void notify(Object key){
      ArrayList<Message> list = waitMap.get(key);
      if(list != null && list.size() > 0){
         Message m = list.remove(0);
         add(m);
         if(list.size() == 0){
            waitMap.put(key, null);
         }
      }
   }
   
   public void notifyAll(Object key){
      ArrayList<Message> list = waitMap.get(key);
      if(list != null && list.size() > 0){
         for(Message m : list){
            add(m);
         }
         list.clear();
         waitMap.put(key, null);
      }
   }
   
   public void sleep(int milli){
      try { Thread.sleep(milli); }
      catch (InterruptedException e){ }; //Do nothing
  }
   
   public void println(Object o){
      System.out.println(o);
   }
   
   public void println(){
      System.out.println();
   }
   
   public void print(Object o){
      System.out.print(o);
   }
   
   public void send(Object o, Actor actor){
      if(actor != null){
         actor.add(new Message(o, this));
      }
   }
   
   public void act() {
      thread = new Thread(this);
      thread.start();
   }
   
   public void stop() {
      thread.interrupt();
   }

   @Override
   public void run() {
      init();
      while(!thread.isInterrupted()){
         if(messageList.size() > 0){
            Message m = get();
            current = m.object;
            receive(m);
         }else{ waiting(); }
      }
      end();
   }
   
   public boolean is(Object o){
      if(current == null){ return false; }
      else{ return current.equals(o); }
   }
   
   public boolean isA(Object o){
      if(current == null || o == null){ return false; }
      else{ return o.getClass().isInstance(current); }
   }
   
   public <E> boolean isA(Class<E> c){
      if(current == null || c == null){ return false; }
      else{ return c.isInstance(current); }
   }

   public abstract void init();
   
   public abstract void receive(Message m);
   
   public abstract void end();

}
Offline Gudradain
« Reply #1 - Posted 2012-08-15 23:59:59 »

PingPong with Scala Actors

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
import scala.actors.Actor;

object PingPong {

case object Ping
case object Pong
case object Stop

class Ping(count: Int, pong: Pong) extends Actor{
   def act(){
       var begin = System.nanoTime();
      var pingsLeft = count - 1;
      pong ! Ping;
      while (true) {
         receive {
         case Pong =>
         if (pingsLeft % 1000 == 0)
            Console.println("Ping: pong")
            if (pingsLeft > 0) {
               pong ! Ping
               pingsLeft -= 1
            } else {
               Console.println("Ping: stop")
               pong ! Stop
               var end = System.nanoTime();
               var delta = end-begin;
               println(delta);
               println(delta/(1000*1000));
               exit()
            }
         }
      }
     
   }
}

class Pong extends Actor{
   def act(){
      var pongCount = 0;
      while (true) {
         receive {
         case Ping =>
         if (pongCount % 1000 == 0)
            Console.println("Pong: ping "+pongCount)
            sender ! Pong
            pongCount = pongCount + 1
         case Stop =>
         Console.println("Pong: stop")
         exit()
         }
      }
   }
}

def main(args: Array[String]){
   val pong = new Pong;
   val ping = new Ping(100000, pong);
   ping.start
   pong.start
}

}


PingPong with my Java actors

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
import main.Actor;
import main.Message;

public class PingPong {
   
   private class Stop { }
   
   private static class Ping extends Actor {
     
      private int pingLeft;
      private Pong pong;
     
      public Ping(int count, Pong pong){
         pingLeft = count;
         this.pong = pong;
      }
     
      @Override
      public void init() {
         pingLeft--;
         send(Ping.class, pong);
      }

      @Override
      public void receive(Message m) {
         if(is(Pong.class)){
            if(pingLeft % 1000 == 0){
               println("Ping: pong");
            }
            pingLeft--;
            if(pingLeft > 0){
               send(Ping.class, pong);
            }else{
               println("Ping: stop");
               send(Stop.class, pong);
               stop();
            }
         }
      }

      @Override
      public void end() { }
     
   }
   
   private static class Pong extends Actor {
     
      private int pongCount = 0;

      @Override
      public void init() { }

      @Override
      public void receive(Message m) {
         if(is(Ping.class)){
            if(pongCount % 1000 == 0){
               println("Pong: ping " + pongCount);
            }
            send(Pong.class, m.source);
            pongCount++;
         }else if(is(Stop.class)){
            println("Pong: stop");
            stop();
         }
      }

      @Override
      public void end() { }

   }
   
   public static void main(String [] args){
      Pong pong = new Pong();
      Ping ping = new Ping(100000, pong);
      pong.act();
      ping.act();
   }

}
Offline ReBirth
« Reply #2 - Posted 2012-08-16 05:07:49 »

Actor's implementation is already made in Java with JADE framework. It has good specification.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline sproingie

JGO Kernel


Medals: 201



« Reply #3 - Posted 2012-08-16 06:29:38 »

Functional Java also has a very high performance Actor implementation, which is essentially the same one that's in Scalaz.  The gold standard for actor systems these days though is Akka, which has both a Scala and Java API.

I see a number of big issues in the Java implementation up top.  The big no-no is the use of a single thread per-actor, which you simply should never be doing for any kind of serious actor system.  Use a thread pool, or better yet make the execution strategy pluggable.

The overloading of wait/notify/notifyAll is probably not a good idea either, since it's too easily confused with the same-named methods on Object, but it's minor compared to that previous one.  I think everyone should explore the actor model, try inventing their own, but then look at an industrial-strength implementation and see what they do differently and why.  


Offline SwampChicken
« Reply #4 - Posted 2012-08-16 06:33:53 »

I don't get it.
Reading the wikipedia (http://en.wikipedia.org/wiki/Actor_model) it sounds like these are nothing more than 'nodes' on a communication network?
I'm not seeing the point? Why have them? They don't appear to be designed do any work at all other then shunt data off to other actors??
Offline sproingie

JGO Kernel


Medals: 201



« Reply #5 - Posted 2012-08-16 06:38:34 »

The point of an actor is that they do useful work whenever they get a message, which results in side effects and usually one or more different messages going to other actors.  The akka docs make it a little clearer than the wikipedia article, and there are also tutorials you can look at.

I use actors in my code by having a web service send a message to an actor to process everything async, then after much expensive backend processing, get back a reply (also via an actor message) to send back.  Inbetween there's a network of service classes that are themselves actors that send each other messages.  

If you've ever used a Message Driven Bean in a JavaEE app, those are also basically actors (with a very heavyweight mailbox).


Offline actual

JGO Coder


Medals: 23



« Reply #6 - Posted 2012-08-16 06:53:59 »

Although I haven't done much work with Actors, I always thought it would be interesting to try and set up GameEntity's as Actors and handle things asynchronously, although you could simply have an Update message that it reacts too.
Offline sproingie

JGO Kernel


Medals: 201



« Reply #7 - Posted 2012-08-16 06:59:40 »

I've been tinkering with a game that does exactly that, and in fact goes totally nuts with the idea and makes every damn thing an actor, including every square on the map (it's a tile-based game).  It even uses libgdx's scene2d actors for the graphics, which are of course a very limited form of actor (they're not even threadsafe) but handy for what they do.  It is not at all small or efficient that way, but hey it runs fast enough on my machine anyway.

Recently I did what I tend to do, tore the whole thing up and started over.  My quest for purity and using scalaz actors will probably be falling by the wayside, and it'll likely be stateful Akka actors this time, become() and all.
Offline SwampChicken
« Reply #8 - Posted 2012-08-16 07:20:29 »

If you've ever used a Message Driven Bean in a JavaEE app, those are also basically actors (with a very heavyweight mailbox).

Cheers for the replies sproingie, I have worked with MQ Series stuff before (ie: set up JMS message queues to chit-chat with AS400 stuff) and did think to myself that perhaps Actors were an alternative to this. I'll take a look at those links of yours soon (on lunchbreak atm)
Offline SwampChicken
« Reply #9 - Posted 2012-08-16 07:23:38 »

Hmmm, just had a crazy thought. Could we use "Actors" to represent NPC's in a game with the comms between them used to implement an enemy hive mind?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline sproingie

JGO Kernel


Medals: 201



« Reply #10 - Posted 2012-08-16 08:07:15 »

Actors localize state, they don't share it very well, because unless you're willing to do some fairly complex stuff around Lamport clocks (or vector clocks), you're not going to get consensus among them on what the current state actually is.  Akka has a few things around STM to help, but if you're really going for a shared AI state, actors are not a great model for that.
Offline Gudradain
« Reply #11 - Posted 2012-08-17 04:23:14 »

I see a number of big issues in the Java implementation up top.  The big no-no is the use of a single thread per-actor, which you simply should never be doing for any kind of serious actor system.  Use a thread pool, or better yet make the execution strategy pluggable.

The overloading of wait/notify/notifyAll is probably not a good idea either, since it's too easily confused with the same-named methods on Object, but it's minor compared to that previous one.  I think everyone should explore the actor model, try inventing their own, but then look at an industrial-strength implementation and see what they do differently and why.  

Thx for pointing that out. I changed it to use a thread pool instead and my performance increased by 6 times. Strangely, the default Scala actors don't seems so fast. For sending and receiving 100000 messages it takes :

Mine : 100 millis (1 micro per message in average)
Scala : 1000 millis (10 micro per message in average)

 persecutioncomplex

Only problems is that by default, my thread pool is always running until I close my JVM. Wonder what is the best solution for that;

- adding start()/stop() method to actor and when at least one actor is running the thread pool is running
- manually starting/stopping the thread pool directly in the code

EDIT : Decided to make an ActorSystem like Akka. You can specify which ActorSystem an Actor use and you need to start/stop the ActorSystem.
Offline sproingie

JGO Kernel


Medals: 201



« Reply #12 - Posted 2012-08-17 07:26:09 »

If you do an empty benchmark with messages that don't get any processing, scala is likely to underperform because it just plain has more overhead.  Even then I'd try benchmarking it against scalaz or akka actors, since scala.actors doesn't have the best latency characteristics.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

pw (14 views)
2014-07-24 01:59:36

Riven (14 views)
2014-07-23 21:16:32

Riven (13 views)
2014-07-23 21:07:15

Riven (15 views)
2014-07-23 20:56:16

ctomni231 (43 views)
2014-07-18 06:55:21

Zero Volt (40 views)
2014-07-17 23:47:54

danieldean (32 views)
2014-07-17 23:41:23

MustardPeter (36 views)
2014-07-16 23:30:00

Cero (50 views)
2014-07-16 00:42:17

Riven (50 views)
2014-07-14 18:02:53
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!