Understanding actor concurrency, Part 2: Actors on the JVM

Actor concurrency libraries for Scala, Groovy, and Java

1 2 Page 2
Page 2 of 2

Listing 7 shows a play() method annotated with @Message that acts as an incoming "play" message. It returns a reply to the caller as a ThrowMessage containing the player's name and move. Each incoming message will be modeled as a method.

Listing 7. Actors Guild example

public abstract class Player extends Actor {
  @Prop
  public abstract String getName();
  public abstract void setName(String name);

  @Message
  public AsyncResult<ThrowMessage> play() {
    return result(new ThrowMessage(getName(), randomMove()));
  }

  // etc
}

In addition to the play() method in Listing 7, you might notice that the Player class is abstract and uses the @Prop annotation on the name property getter and setter. Actors Guild includes a dependency-injection framework for the creation and setup of the actor instances.

The runtime instrumentation that Actors Guild does is an advantage over Kilim and ActorFoundry because it simplifies the integration into existing development environments. In a Java library, the use of methods to handle message receive is a very natural mapping and works well. Between Actors Guild and ActorFoundry, I prefer the Actors Guild approach, which pushes you toward a single class defining the message and away from reflective strings.

However, I don't like having dependency injection mixed up in my actor code. Popular frameworks such as Guice and Spring already handle dependency injection in robust ways; I am wary of a new similar solution wedged into a library with different goals. For example, needing to declare actor classes as abstract classes and not being allowed to define a constructor are drawbacks. These constraints mean that to do any testing on the actor, you must subclass it and provide the abstract methods or else use the Actors Guild factories to generate the actor instance.

Building actor-like behavior with Jetlang

Finally, we look at the Jetlang library, which technically does not provide an actor framework but rather a set of building blocks from which actor-like functionality (as well as other architectures) can be assembled. Jetlang is based on the older Retlang library for .NET.

Jetlang has three primary concepts:

  • Fiber - a lightweight thread-like construct
  • Channel - a way to pass messages between Fibers
  • Callback - a function to be invoked when a message arrives on a Channel in the context of a Fiber

These building blocks are enough to assemble the concept of an actor. I created an Actor class, shown in Listing 8, that holds a Fiber, a Channel to use as an inbox, and a Callback function to call when messages arrive in the inbox. It also provides a send() method to put a message on the actor's inbox queue.

Listing 8. An actor in Jetlang

public class Actor<T> {
  private final Channel<T> inbox;
  private final Fiber fiber;
  private final Callback<T> callbackFunction;

  public Actor(Fiber fiber, Channel<T> inbox, Callback<T> callbackFunction) {
    this.fiber = fiber;
    this.inbox = inbox;
    this.callbackFunction = callbackFunction;
  }

  public Channel<T> inbox() {
    return this.inbox;
  }

  public Fiber fiber() {
    return this.fiber;
  }

  public Callback<T> callbackFunction() {
    return this.callbackFunction;
  }

  public void send(T message) {
    this.inbox.publish(message);
  }
}

I then created an ActorFactory, shown in Listing 9, that can be used to create new actors from a Callback function. This simplifies the process of obtaining a Fiber and creating the Actor.

Listing 9. ActorFactory

public class ActorFactory {
  private final ExecutorService threads;
  private final PoolFiberFactory fiberFactory;

  public ActorFactory() {
    threads = Executors.newCachedThreadPool();
    fiberFactory = new PoolFiberFactory(threads);
  }

  public Fiber startFiber() {
    Fiber fiber = fiberFactory.create();
    fiber.start();
    return fiber;
  }

  public <T> Channel<T> createInbox() {
    return new MemoryChannel<T>();
  }

  public <T> Actor<T> createActor(Callback<T> actorCallback) {
    Fiber fiber = startFiber();
    Channel<T> inbox = createInbox();
    inbox.subscribe(fiber, actorCallback);
    return new Actor<T>(fiber, inbox, actorCallback);
  }
}

The actor classes are then defined as a Callback, as in Listing 10.

Listing 10. Jetlang Player callback function

public class Player implements Callback<PlayMessage> {
  private final String name;

  public Player(String name) {
    this.name = name;
  }

  public void onMessage(PlayMessage message) {
    message.getResponseActor().send(
      new ThrowMessage(name, randomMove()));
  }

  // etc
}

In conclusion

For a pure actor approach, I find Scala to be the strongest implementation in terms of both similarity to Erlang and maturity. Among the Java alternatives, I like the Jetlang library the best in some ways, because it provides a consistent model that doesn't feel awkward or unnatural in Java (in either tooling or usage). It requires a bit more thinking initially because it's farthest from the original actor model. All of these implementations are relatively young, and I believe they will continue to evolve in interesting ways. If you have the freedom to try something new with the approach to your project, do your own assessment to determine if one of these solutions is a good option.

Alex Miller is a tech lead at Terracotta Inc, the maker of the open-source Java clustering product Terracotta. Previously, Alex worked at BEA Systems on the AquaLogic product line and was Chief Architect at MetaMatrix. Alex blogs at Pure Danger Tech, tweets as @puredanger and speaks at user groups, conferences, and the No Fluff Just Stuff tour. He has an enduring love for his wife and three children, music, and nachos.

Learn more about this topic

More from JavaWorld

1 2 Page 2
Page 2 of 2