Channels ▼
RSS

Parallel

JVM Concurrency and Actors with GPars



This article is the first in a new monthly series, which presents and examines an open-source project of interest to serious developers. We begin this series with GPars, a concurrency framework for the JVM that greatly facilitates actor-based programming. — Andrew Binstock


The GPars project makes concurrency easily accessible and safe to use on the JVM. In this article. I'll focus on the actor model part of GPars. We'll experiment with the various types of actors that GPars provides and see their typical usages. All the project‘s functionality is available from Java and from Groovy, with which GPars is most often associated.

Introduction to Actors

The concept of actors as an approach to organizing concurrent activities has recently gained new popularity (thanks to the Scala, Erlang, and other programming languages). Actors provide a programming model that gives stronger guarantees about concurrent code when compared with the traditional shared-memory-based abstraction. Actors are essentially well encapsulated active objects, which can only communicate by sending one another immutable messages asynchronously. Whatever state an actor holds internally, it cannot be accessed from outside the actor except by sending a message to the actor and receiving its reply.

Because actors handle incoming messages asynchronously, they themselves need to be active. One way to make an object active is to dedicate a system thread to it. With actors, however, you frequently need to scale beyond the number of threads available on the system. That's why actor implementations usually share threads among actors. If an actor has no work to do, it doesn't consume system threads.

Actors guarantee that there's never more than one thread at a time processing the messages of each actor. The actor's state can thus be modified safely from within its body without any other extra (synchronization or locking) effort. The actor's own code is implicitly thread-safe.

Actors in GPars

GPars is an open-source project that provides a Java implementation of actors, with a convenient API for direct use either from Groovy or Java. Actors perform three specific operations — they send messages, receive messages, and create new actors. Although not enforced by GPars, messages should be immutable or at least follow a hands-off policy in which the sender never touches the messages after the message has been sent off. This code shows an example of actors at work.

    final class MyCounterActor extends DynamicDispatchActor 
    {
        private int counter = 0;

        void onMessage(String message) {
            log("Received a string");
            counter += message.length();
        }

        void onMessage(Integer message) {
            log("Received an integer");
            counter += message;
        }

        void onMessage(Boolean message) {
            log("Received a boolean");
            reply(counter);
        }
    }

public class DecryptorTest 
{
    public static void main(String[] args) throws InterruptedException {
       	Actor counter = new MyCounterActor().start();
       	counter.send("Hello");
     	System.out.println("Current value is: " + 
		counter.sendAndWait(true));
      	counter.stop();
       	counter.join();
    }
}

In the Java examples in the preceding code, you find all the fundamental actor operations. Actors are being created, started, and stopped. Messages are being sent, received, and replied to asynchronously. The message themselves are immutable values. Let's go through these in detail now.

Creating Actors

Actors can be created either by instantiating the appropriate class (see main()) or using appropriate factory methods. The former approach is preferred when instantiating actors in Java, while the latter approach feels more Groovy-like.

Sending messages

Messages can be sent to actors using the send() method. Alternatively, the << operator or the implicit call() method can be used, when calling the actor from Groovy.

actor.send('Message 1') //in Java
actor << 'Message 2'    //using the << operator
actor 'Message 3'       //using the implicit call() method

A family of sendAndWait() methods is available to block the caller until a reply from the actor is available. The reply message is returned from the sendAndWait() method as a return value.

def reply1 = actor.sendAndWait('Message 4')
def reply2 = actor.sendAndWait('Message 5', 10, TimeUnit.SECONDS)

The sendAndContinue() method allows the caller to continue its processing while the supplied closure is waiting for a reply from the actor.

friend.sendAndContinue 'I need money!', { money -> pocket(money) }
println 'Look, I can continue while my friend is collecting money for me!'

Replying

To send a reply to the currently processed message actor, use the reply method. The reply is sent back asynchronously just like normal messages.

Forwarding

When sending a message, a different actor can be specified as a recipient for potential replies. Since actors should rarely block waiting for replies, forwarding helps avoid architectural bottlenecks.

public class DecryptorTest {
    public static void main(String[] args) {
        Actor decryptor = new Decryptor().start();
        Actor console = new Console().start();
        
        //Specify an actor to send the replies to
        decryptor.send("lellarap si yvoorG", console);      }
}

class Decryptor extends DynamicDispatchActor {
    void onMessage(String message) {
        reply(reverse(message));
    }
}

//This actor will print out decrypted messages,
//since the replies are forwarded to it
class Console extends DynamicDispatchActor {
    void onMessage(String message) {
        System.out.println("Decrypted message: " + message);
    }
}

Types of Actors

In general, you can find two types of actors in the wild: those that hold implicit state, and those that don't. GPars gives you both options. Stateless actors, represented in GPars by the DynamicDispatchActor and the ReactiveActor classes, do not keep track of what messages have arrived previously. This is the type of actor presented in the examples so far. You can think of these as flat message handlers: They process messages as they arrive. Any state-based behavior has to be implemented by the programmer.

Stateful actors, on the other hand, represented in GPars by the DefaultActor class, allow the actor to handle the implicit state directly. After receiving a message, the actor moves into a new state with different set of handlers to handle future messages.

A stateful actor doesn't provide independent message handlers. Instead, a stateful actor defines a body — a block of code to execute when the actor starts. Inside the body, the actor may check for messages any time by calling the react method. Depending on the actual message contents, the actor executes different actions.

Stateless Actors

DynamicDispatchActor repeatedly scans for received messages and dispatches them to one of the onMessage() methods defined in the actor. The DynamicDispatchActor leverages the Groovy dynamic method dispatch mechanism under the covers. Even if the class is implemented in Java, the messages are dispatched dynamically to the right message handler:

final class Decryptor extends DynamicDispatchActor {
    void onMessage(String message) {
        log('Decrypting a string');
        reply(message.reverse());
    }

    onMessage(Integer message) {
        log('Decrypting an integer');
        reply(message + 12);
    }
}

Additionally, when used from Groovy, message handlers can be added at runtime to alter the behavior dynamically:

myActor.when {BigDecimal num -> log 'Received a BigDecimal'}
myActor.when {Float num -> log 'Got a float'}

The ReactiveActor class allows for more event-driven-like approach. You can also view it as an asynchronous function of a sort. When a reactive actor receives a message, the actor's body is run with the message as a parameter. The result of this is then returned in reply.

final def doubler = reactor {
    2 * it
}

println 'Double of 10 = ' + doubler.sendAndWait(10)

Defining a Reactor in Java is only a little more verbose:

Closure handler = new ReactorMessagingRunnable<Integer, Integer>() {
    @Override
    protected Integer doRun(final Integer value) {
        return value * 2;
    }
};
final Actor doubler = reactor(handler);

Stateful Actors

The stateful type of actors will probably be familiar to those who know actors in Scala.

def actor = actor {
    loop {
        log 'Waiting for a gift'
        react {gift ->
            if (myWife.likes gift) reply 'Thank you!'
            else {
                reply 'Try again, please'
                react {anotherGift ->
                    if (myChildren.like gift) reply 'Thank you!'
                }
            }
        }
    }
}

As you can see, you create new actors with the actor() factory method passing in the actor's body as a closure parameter. Once started, actors run their body top to bottom, reading messages from their mail-boxes along the way. Inside the actor's body you can use loop() to iterate, react() to receive messages, and reply() to send a reply to the currently processed message.

Notice the implicit state transitions of the actor. The first message that arrives is handled by a different handler than the second message in the sequence.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video