Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
As I previously mentioned in “Free lunches, mousetraps and the Actor model“, Edward A. Lee wrote an interesting article entitled “The Problem with Threads” in which he advocates leveraging the actor model in popular languages (such as in Java) as opposed to adopting an entire new paradigm (like Erlang). He states:
We should not replace established languages. We should instead build on them.
It appears that more than a few hip people agree with his line of thinking. It turns out there are quite a few options available for leveraging the actor model in Java. That is, aside from alternative languages like Scala, which supports actors and Groovy with GPars, there’s framework’s like Kilim, ActorFoundry, Actors Guild, and jetlang.
I ended up employing Kilim at a client side over a year ago to replace a thread based computational model. At the time, GPars was in its early stages and I was specifically looking for a speed up in application performance. The multi-threaded application was taking roughly 5 hours to complete.
In the end, the speed up attributed to Kilim (or indirectly leveraging its actor model) was hardly noticeable (some aspects of Kilim were noticeably faster though — such as spawning a task was quite fast as opposed to spawning a normal threads) as the real performance gain was leveraged by reducing and improving database queries (as usual, performance issues were essentially IO bound); nevertheless, the prime benefit of Kilim, which at the time I had overlooked, was the notion of a mailbox. That is, in the actor model, processes can share data more safely.
There are quite a few different implementations and ways to facilitate message passing in various languages and platforms, but to me, the actor model’s mailbox notion is quite intuitive. In Kilim’s actor model, messages are passed between processes via a Mailbox — in many ways, you can think of it as a queue. Processes can put items into a mailbox and also pull items from a mailbox in both a blocking and non-blocking manner (blocking of the underlying process not a thread).
As an example of leveraging mailboxes in Kilim, I wrote two actors (a Husband and a Wife) that extend from Kilim’s Task type. Previous versions of Kilim had an Actor type; however, as of version 0.6, Task is the way to go.
import kilim.Mailbox;
import kilim.Pausable;
import kilim.Task;
public class Husband extends Task {
private Mailbox<Message> mailbox;
public Husband(Mailbox<Message> mailbox) {
super();
this.mailbox = mailbox;
}
@Override
public void execute() throws Pausable, Exception {
while (true) {
System.out.println("Husband listening...");
Message msg = mailbox.get(); // blocks
if (msg.getReceipient() == Message.HUSBAND) {
System.out.println("Husband hears: " + msg.getMessage());
Message reply = new Message(Message.WIFE, "Yes, dear");
mailbox.putnb(reply);
}
Task.sleep(1000);
}
}
}
Under the covers, Kilim works by weaving bytecode so as to control Task types and facilitate their safe interaction — specifically, Kilim’s weaver is looking for methods that throw the Pausable type (previous versions used the @Pausable annotation).
In the Husband class, a few things are going on — first, the instance waits for a message from a shared mailbox. In this case, I used a blocking get call (as in reality, a husband really doesn’t do anything else but waits for orders (I mean requests) from his wife). When a message is picked up, the instance checks to see if it was intended for it (in many cases, wife instances can communicate with children types sending messages like “clean your room” or “brush your hair”).
Once a Message type (which, in this case, is not a Kilim type) is determined to be sent to a Husband instance, the Husband type replies appropriately by creating a Message and placing it into the mailbox instance. Finally, the sleep call is just placed to facilitate reading console output.
The Wife class is similar (expect that she doesn’t wait to listen…). Like the Husband instance, this class creates a Message (in the form of a Honey Do) and sends it off via the shared MailBox; however, she doesn’t wait around — that is, the instance uses the putnb call, which is non-blocking. What’s more, she’ll also attempt to see if anything is in the mailbox for her, but in her case, she also uses a non-blocking call (getnb) like so:
import kilim.Mailbox;
import kilim.Pausable;
import kilim.Task;
public class Wife extends Task {
private Mailbox<Message> mailbox;
public Wife(Mailbox<Message> mailbox) {
super();
this.mailbox = mailbox;
}
@Override
public void execute() throws Pausable, Exception {
while (true) {
Message request = new Message(Message.HUSBAND,
"Please do x, y, and z today, husband.");
mailbox.putnb(request);
Task.sleep(1000);
Message msg = mailbox.getnb(); // no block
if (msg != null && msg.getReceipient() == Message.WIFE) {
System.out.println("Wife hears: " + msg.getMessage());
}
}
}
}
As you can see, if there is a message waiting for her, she’ll hear it, otherwise, she moves on and requests her husband to do something else.
Lastly, everything is coordinated by a simple driver class containing a main method like so:
import kilim.Mailbox;
import kilim.Task;
public class HoneyDo {
public static void main(String[] args) {
Mailbox<Message> sharedMailbox = new Mailbox<Message>();
Task wife = new Wife(sharedMailbox);
Task husband = new Husband(sharedMailbox);
husband.start();
wife.start();
}
}
Note how a Mailbox instance is created for my custom Message type; what’s more, the sharedMailbox is then shared between both the wife and husband instances. Lastly, things are started via the start method.
Running this hip example yields the following output (remember, your exact output will most likely look different; however, the logic sequence of activities will line up. That is, the wife requests things be done and the husband responds with “yes, dear”, which the wife hears).
Husband listening...
Husband hears: Please do x, y, and z today, husband.
Husband says: Yes, dear
Husband listening...
Wife hears: Yes, dear
Husband hears: Please do x, y, and z today, husband.
Husband says: Yes, dear
Husband listening...
Wife hears: Yes, dear
Husband hears: Please do x, y, and z today, husband.
Husband says: Yes, dear
Wife hears: Yes, dear
Husband listening...
Husband hears: Please do x, y, and z today, husband.
Husband says: Yes, dear
Husband listening...
Wife hears: Yes, dear
Husband hears: Please do x, y, and z today, husband.
Husband says: Yes, dear
Wife hears: Yes, dear
Husband listening...
Husband hears: Please do x, y, and z today, husband.
Husband says: Yes, dear
The actor model facilitates concurrent programming by allowing a safer mechanism for message passing between processes (or actors). Implementations of this model vary between languages and frameworks — I suggest checking out Erlang‘s actors followed by Scala‘s as each implementation is quite neat given their respective syntax.
In the end, if you want to leverage plain Jane Java actors, then have a look at Kilim (or one of the other frameworks available) — just make sure you’ve finished your honey do list first, man.
Looking to spin up Continuous Integration quickly? Check out www.ciinabox.com.