Building distributed systems for concurrent and scalable Java applications The actor model is a message-passing paradigm that resolves some of the major challenges of writing concurrent, scalable code for today’s distributed systems. In this installment of Open source Java projects, Steven Haines introduces Akka, a JVM-based toolkit and runtime that implements the actor model. Get started with a simple program that demonstrates how an Akka message passing system is wired together, then build a more complex program that uses concurrent processes to compute prime numbers. If you have experience with traditional concurrency programming, then you might appreciate the actor model, which is a design pattern for writing concurrent and scalable code that runs on distributed systems. Briefly, here’s how the actor model works: Rather than invoking an object directly, you construct a message and send it to the object (called an actor) by way of an actor reference. The actor reference stores the message in a mailbox. When a thread becomes available, the engine running the actor delivers that message to its destination object. When the actor completes its task, it sends a message back to the originating object, which is also considered an actor. You might suspect that the actor model is more of an event-driven or message-passing architecture than a strict concurrency solution, and you would be right. But Akka is a different story: an actor model implementation that enables developers to accomplish impressively high concurrency at a very low overhead. Re-thinking concurrency with Akka (and Scala) Actors provide a simple and high-level abstraction for concurrency and parallelism. They support asynchronous, non-blocking, and high-performance event-driven programming, and they are lightweight processes. (Akka’s founding company, Typesafe, claims up to 2.7 million actors per gigabyte of RAM.) Akka and other message-passing frameworks offer a workaround to the challenges of multithreaded programming (see the sidebar “What’s wrong with multithreaded programming?”), while also meeting some of the emergent needs of enterprise programming: Fault tolerance: Supervisor hierarchies support a “let-it-crash” semantic and can run across multiple JVMs in a truly fault tolerant deployment. Akka is excellent for highly fault-tolerant systems that self-heal and never stop processing. Location transparency: Akka is designed to run in a distributed environment using a pure message-passing, asynchronous strategy. Transactors: Combine actors with software transaction memory (STM) to form transactional actors, which allow for atomic message flows and automatic retry and rollback functionality. Since the actor model is relatively new to most Java developers, I’ll explain how it works first, then we’ll look at how it’s implemented in Akka. Finally, we’ll try out the Akka toolkit in a program that computes prime numbers. What’s wrong with multithreaded programming? Multithreaded programming basically means running multiple copies of your application code in their own threads and then synchronizing access to any shared objects. While it’s a complex issue, multithreaded programming has three major fault lines: Shared objects: Whenever multiple threads access shared objects there is always the danger that one thread will modify the data upon which another thread is operating underneath it. Typically, developers solve this issue by encapsulating the dependent functionality in a synchronized method or synchronized block of code. Numerous threads may attempt to enter that code block, but only one thread will get through; the others will wait until it completes. This approach protects your data, but it also creates a point in your code where operations occur serially. Deadlock: Because we need to synchronize access to code that operates on shared resources, deadlock sometimes occurs. In code synchronization (as described above), the first thread that enters a synchronized block obtains the lock, which is owned by the object on which the operation is synchronized. Until that lock is released, no other thread is permitted to enter that code block. If thread 1 obtains the lock to synchronized block 1, and thread 2 obtains the lock to synchronized block 2, but it happens that thread 1 needs access to synchronized block 2 and thread 2 needs access to synchronized block 1 then the two threads will never complete and are said to be deadlocked. Scalability: Managing multiple threads in a single JVM is challenging enough, but when you need to scale the application across multiple JVMs the problem increases by an order of magnitude. Typically, running concurrent code across multiple JVMs involves storing shared state in a database and then relying on the database to manage concurrent access to that data. Akka and the actor model Akka is an open source toolkit and runtime that runs on the JVM. It’s written in Scala (a language often touted for concurrency) but you can use Java code (or Scala) to call all of its libraries and features. The principle design pattern that Akka implements is the actor model, as shown in Figure 1. Figure 1. Actor model concurrency in Akka (click to enlarge) The Akka toolkit implements the following actor model components: ActorSystem is the glue that wires Actors, ActorRefs, MessageDispatchers, and Mailboxes together. Programmers use the ActorSystem to configure the desired implementation of each of these components. ActorRef is an addressable location. Messages destined for an underlying Actor are sent to an ActorRef. MessageDispatcher is the engine that runs the other parts of the ActorSystem and schedules all its activities. The MessageDispatcher‘s main responsibility is to run Mailboxes. When an ActorRef receives a message, it dispatches it to the MessageDispatcher, which puts that message in the message queue for the destination Mailbox. Mailbox is the component that holds Actors. Every Actor has its own mailbox. When the mailbox receives a slice of time to run, it takes a message from the message queue and invokes the Actor with the message. MessageQueue is the first recipient of new messages. When the MessageDispatcher receives a message from the ActorRef, it delivers that message to the mailbox’s Message Queue, where it sits until the mailbox receives CPU cycles to run. Actor is an object that provides a receive method, which is invoked by the mailbox when it has a message to process. To summarize: When you want to invoke the functionality of an actor in Akka, you send a message to the ActorRef. The ActorRef dispatches it to a MessageDispatcher, which delivers it to a Mailbox‘s MessageQueue. When the Mailbox receives a thread time slice from the MessageDispatcher, it retrieves the message from the MessageQueue and then invokes the Actor‘s receive method, passing it the message. Load balancing and horizontal scalability Akka’s message-passing process might sound convoluted but it affords Akka two especially powerful benefits: Load balancing: The MessageDispatcher can be configured with a one-to-one mapping of threads to actors, but it doesn’t have to be. This means that the MessageDispatcher can maintain a thread pool that is configured to a sustainable quantity for the hardware on which it is running; but it can have far more actors than it does threads. Your application is still limited by the number of threads running actors, but unless all actors are always busy and using all of their CPU time-slices, this level of abstraction allows Akka to take advantage of actor downtime and support more load than a standard threading model could do. Horizontal scalability: While vertical scalability refers to building out bigger and bigger machines that can individually do more work, horizontal scalability refers to running multiple machines together to complete the work. Akka can easily support horizontal scalability because your application never directly interacts with Actors, but instead interacts with ActorRefs. The two variations of an ActorRef are local and remote. Local ActorRefs deliver messages to a MessageDispatcher running in the same JVM. Remote ActorRefs send messages to a MessageDispatcher running on a different machine. This proxy/stub model is key to distributed computing; Akka exploits it to gain horizontal scalability without requiring your application to change beyond its Akka configuration. Actor hierarchies and the ActorPath All of this leads to another important concept in the Actor model, and in Akka specifically: Actor hierarchies. All Actors belong to an actor hierarchy in the ActorSystem. Actors that can be directly created are called Top Level Actors. The ActorSystem defines two separate hierarchies: a system hierarchy to manage internal tasks and a user hierarchy to define Actors for your application. The ActorSystem defines a user-level root actor, named Guardian, under which all top-level actors are created. Top-level Actors are then free to create their own child Actors (which can then create their own child Actors, and so forth). Together, the Guardian, Actors, and their children comprise the user hierarchy. The final concept that you need to understand in order to effectively use Akka is that all Actors are named, and there is an addressing scheme for locating Actors, called the ActorPath. Here’s an example of an ActorPath: akka://MySystem@MyServer:MyPort/user/MyTopLevelActor/MyChild Its components are as follows: akka is the name of the scheme, so it will always be “akka“. MySystem is the name of the ActorSystem that owns the Actor being addressed. MyServer:MyPort is the server and port for the server hosting the Actor (e.g.,192.168.1.1:1234). user is the name of the guardian, which is always “user” for non-system Actors MyTopLevelActor is the name given to a top-level actor when you create an ActorSystem. MyChild is the name of a child actor. You’ll use this name if you’re trying to access an actor that is a child of a specified top-level Actor. You can continue down the hierarchy until you reach the actor you’re looking for. Open source Java projects Read more enterprise technology profiles in this series: Storm: Parallel realtime computation for unbounded data streams GitHub: Social coding with Git TomEE: Scale to enterprise with the Java EE 6 Web container built on Tomcat Setting up Akka Akka can run embedded inside your application or as a standalone server or set of servers. The beauty of Akka is that the code looks the same either way. For the purposes of this article we’ll build Actors and ActorSystems inside of an application. To build and run Akka applications using Maven, you only need to include the following dependency in your POM file (note that the latest artifact as of this writing was version 2.1.2): <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-actor_2.10</artifactId> <version>2.1.2</version> </dependency> If you are not using Maven, you’ll need to download Akka from the Typesafe homepage and add lib/akka/akka-actor_2.10-2.1.2.jar to your CLASSPATH. Hello, Message! In this section I’ll demonstrate the simplest way to build an Akka application. My generic “Hello World” app will use the default configuration for most options and will only define a single actor. This way you’ll learn the bare minimum that is required to build and execute an actor before we try writing a more complex program. You can download the project source code for this article at any time. As I mentioned earlier, there are several ways to build an Akka application. This example uses Maven to manage Akka as a dependency and runs Akka embedded inside of the application itself. Listing 1 shows the contents of the Maven pom.xml file. Listing 1. pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.geekcap.akka</groupId> <artifactId>AkkaSample</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>AkkaSample</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.geekcap.akka.HelloActor</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>install</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-actor_2.10</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </project> Note that this POM file includes Akka version 2.1.2 as a dependency. In the build section, it sets the Java build version to 1.6, tells Maven to package the dependent JAR files into the target lib directory, and defines a main class to execute. Akka operates by passing messages to actors, so Listing 2 shows the source code for HelloMessage.java. Listing 2. HelloMessage.java package com.geekcap.akka; import java.io.Serializable; public class HelloMessage implements Serializable { private String message; public HelloMessage(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } The HelloMessage class is a simple POJO that implements the Serializable interface. This is important because if you do move your Actors to another machine your messages must be serializable. Making all messages serializable ensures that you will not have issues later on. Listing 3 shows the meat of the Akka code in the HelloActor class. Listing 3. HelloActor.java package com.geekcap.akka; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.actor.UntypedActor; public class HelloActor extends UntypedActor { public void onReceive(Object message) { if( message instanceof HelloMessage) { System.out.println( "My message is: " + ( (HelloMessage)message ).getMessage() ); } } public static void main( String[] args ) { ActorSystem actorSystem = ActorSystem.create( "MySystem" ); ActorRef actorRef = actorSystem.actorOf( new Props( HelloActor.class ), "myActor" ); actorRef.tell( new HelloMessage( "Hello, Akka!" ) ); try { Thread.sleep( 1000 ); } catch( Exception e ) {} actorSystem.stop( actorRef ); actorSystem.shutdown(); } } Listing 3 defines the HelloActor class, which extends the UntypedActor base class. This class has a single method that needs to be implemented: onReceive(). The onReceive() method is invoked by the HelloActor‘s Mailbox when the MessageDispatcher gives the Mailbox a thread time-slice, if there is a message waiting in the Mailbox‘s MessageQueue. The message is passed as an object so we check to see if the message received is a HelloMessage. If the message is a HelloMessage, then the HelloActor will invoke the HelloMessage‘s getMessage() method to print out the message. The HelloActor class has a main() method that sets up the ActorSystem. It creates an ActorSystem named “MySystem” and then creates an ActorRef that points to the HelloActor class named “actorRef“. The main() method sends the actorRef a new HelloMessage instance with the message “Hello, Akka!” by invoking its tell() method. The tell() method is the mechanism for sending messages to actors asynchronously. The main() method then sleeps for a second and finally shuts down Akka. It shuts down by first telling the ActorSystem to stop the HelloActor actor reference, and then telling the ActorSystem it shut itself down. HelloMessage doesn’t demonstrate how to create a concurrent application using Akka, but it does demonstrate how to create various actor model components (ActorSystem, Actor, and ActorRef), and send messages to an Actor through the ActorRef. The prime number computing program in the next section will demonstrate Akka’s concurrency model. You’ll also get to see a better approach to shutting down the actor system, using a listener actor. Computing prime numbers with Akka The example application in this section is a complete Akka application that computes prime numbers for a given range. For example, if we were to list all of the prime numbers between 1 and 10, we would expect the following output: 1, 2, 3, 5, 7. This program won’t be particularly challenging to code, but it will demonstrate how to do following: Create a master actor Create a round-robin router to distribute work across multiple worker actors Communicate between worker actors and the master actor Communicate between the master actor and a listener Not bad for a simple number cruncher! Figure 2 shows all the moving parts involved in the PrimeCalculator program. Figure 2. An actor-based system for computing prime numbers with Akka (click to enlarge) As shown in Figure 2, the PrimeCalculator provides a calculate() method that sends a number-range message to the PrimeMaster class. The PrimeMaster divides this number range into 10 units and distributes them across 10 PrimeWorkers. The PrimeWorkers complete their work and then publish a Result message back to the PrimeMaster. The PrimeMaster aggregates all 10 Result responses, then publishes the aggregate Result object to the PrimeListener. The PrimeListener writes the results to the screen and then shuts down the ActorSystem. In the next sections I’ll discuss each of the main components of the PrimeCalculator in detail. NumberRangeMessage and Result We’ll start with the NumberRangeMessage and Result message classes, which are are simple serializable POJOs that contain data to be passed between actors. Listing 4. NumberRangeMessage.java package com.geekcap.akka.prime.message; import java.io.Serializable; public class NumberRangeMessage implements Serializable { private long startNumber; private long endNumber; public NumberRangeMessage(long startNumber, long endNumber) { this.startNumber = startNumber; this.endNumber = endNumber; } public long getStartNumber() { return startNumber; } public void setStartNumber(long startNumber) { this.startNumber = startNumber; } public long getEndNumber() { return endNumber; } public void setEndNumber(long endNumber) { this.endNumber = endNumber; } } Listing 5. Result.java package com.geekcap.akka.prime.message; import java.util.ArrayList; import java.util.List; public class Result { private List<Long> results = new ArrayList<Long>(); public Result() { } public List<Long> getResults() { return results; } } Both of these classes are pretty self-explanatory. PrimeWorker The PrimeWorker shown in Listing 6, below, examines the number range passed to it and returns a list of all prime numbers within that range. Listing 6. PrimeWorker.java package com.geekcap.akka.prime; import akka.actor.UntypedActor; import com.geekcap.akka.prime.message.NumberRangeMessage; import com.geekcap.akka.prime.message.Result; public class PrimeWorker extends UntypedActor { /** * Invoked by the mailbox when it receives a thread timeslice and a message is available * * @param message The message to process */ public void onReceive( Object message ) { // We only handle NumberRangeMessages if( message instanceof NumberRangeMessage ) { // Cast the message to a NumberRangeMessage NumberRangeMessage numberRangeMessage = ( NumberRangeMessage )message; System.out.println( "Number Rage: " + numberRangeMessage.getStartNumber() + " to " + numberRangeMessage.getEndNumber() ); // Iterate over the range, compute primes, and return the list of numbers that are prime Result result = new Result(); for( long l = numberRangeMessage.getStartNumber(); l <= numberRangeMessage.getEndNumber(); l++ ) { if( isPrime( l ) ) { result.getResults().add( l ); } } // Send a notification back to the sender getSender().tell( result, getSelf() ); } else { // Mark this message as unhandled unhandled( message ); } } /** * Returns true if n is prime, false otherwise * * @param n The long to check * * @return True if n is prime, false otherwise */ private boolean isPrime( long n ) { if( n == 1 || n == 2 || n == 3 ) { return true; } // Is n an even number? if( n % 2 == 0 ) { return false; } //if not, then just check the odds for( long i=3; i*i<=n; i+=2 ) { if( n % i == 0) { return false; } } return true; } } PrimeWorker is the class that performs the labor of computing prime numbers. It inspects a received message and if it is a NumberRangeMessage it processes it. Otherwise PrimeWorker invokes the unhandled() method to notify its parent that it did not handle the message. PrimeWorker‘s logic is actually rather simple: it iterates over the numbers in its number range and then invokes its isPrime() method on each number. After it completes processing it reports back to the PrimeMaster, which it does by calling getSender() to retrieve an ActorRef to the actor that sent the message (PrimeMaster), and then invoking its tell() method with the results. PrimeWorkers run asynchronously, so this “callback” lets the PrimeMaster know that a given worker has completed its task. PrimeMaster The PrimeMaster divides the number range passed to it into 10 relatively even chunks and then distributes them to its PrimeWorker router. Listing 7. PrimeMaster.java package com.geekcap.akka.prime; import akka.actor.ActorRef; import akka.actor.Props; import akka.actor.UntypedActor; import akka.routing.RoundRobinRouter; import com.geekcap.akka.prime.message.NumberRangeMessage; import com.geekcap.akka.prime.message.Result; public class PrimeMaster extends UntypedActor { private final ActorRef workerRouter; private final ActorRef listener; private final int numberOfWorkers; private int numberOfResults = 0; private Result finalResults = new Result(); public PrimeMaster( final int numberOfWorkers, ActorRef listener ) { // Save our parameters locally this.numberOfWorkers = numberOfWorkers; this.listener = listener; // Create a new router to distribute messages out to 10 workers workerRouter = this.getContext() .actorOf( new Props(PrimeWorker.class ) .withRouter( new RoundRobinRouter( numberOfWorkers )), "workerRouter" ); } @Override public void onReceive( Object message ) { if( message instanceof NumberRangeMessage ) { // We have a new set of work to perform NumberRangeMessage numberRangeMessage = ( NumberRangeMessage )message; // Just as a demo: break the work up into 10 chunks of numbers long numberOfNumbers = numberRangeMessage.getEndNumber() - numberRangeMessage.getStartNumber(); long segmentLength = numberOfNumbers / 10; for( int i=0; i<numberOfWorkers; i++ ) { // Compute the start and end numbers for this worker long startNumber = numberRangeMessage.getStartNumber() + ( i * segmentLength ); long endNumber = startNumber + segmentLength - 1; // Handle any remainder if( i == numberOfWorkers - 1 ) { // Make sure we get the rest of the list endNumber = numberRangeMessage.getEndNumber(); } // Send a new message to the work router for this subset of numbers workerRouter.tell( new NumberRangeMessage( startNumber, endNumber ), getSelf() ); } } else if( message instanceof Result ) { // We received results from our worker: add its results to our final results Result result = ( Result )message; finalResults.getResults().addAll( result.getResults() ); if( ++numberOfResults >= 10 ) { // Notify our listener listener.tell( finalResults, getSelf() ); // Stop our actor hierarchy getContext().stop( getSelf() ); } } else { unhandled( message ); } } } The PrimeMaster is a bit more complicated than the PrimeWorker. In its constructor it creates a RoundRobinRouter that distributes messages to a pool of PrimeWorkers. It creates the RoundRobinRouter by first creating the actor with the actorOf() method, but then invoking the withRouter() method on the returned ActorRef. A round-robin strategy distributes messages to each actor in turn. When it reaches the end of its list it starts over again at the beginning. The PrimeMaster‘s onReceive() method needs to handle two types of messages: a NumberRangeMessage that is received from the PrimeCalculator to start the process and a Result message that is received from each PrimeWorker when it completes its work. The NumberRangeMessage handler divides the work into 10 chunks and dispatches those chunks to the PrimeWorkers by invoking the router’s tell() method. The router then handles distributing those messages to workers in a round-robin fashion. An additional complexity in this handler is that it might miss some messages at the end when the range is divided into chunks of 10 (unless the number of numbers to examine is evenly divisible by 10, in which case there will be a remainder that needs to be accounted for). The last handler ensures that all the numbers are processed by including them all to the end of the range. This is not the best way to distribute numbers to workers because smaller numbers will take less processing time than larger numbers, but it does illustrate the process without adding additional complexity. The Result message handler adds the results from the worker to its final result list and then checks to see if it has received messages from all the workers. Since NumberRangeMessage handler divided the work into 10 chunks, the Results handler counts responses and stops when it has received 10 responses. When it complete it invokes the listener’s tell() method with the final results, and then it shuts itself down. PrimeListener Listing 8 shows the PrimerListener, which is the actor that receives the final results from the PrimeMaster. Listing 8. PrimeListener.java package com.geekcap.akka.prime; import akka.actor.UntypedActor; import com.geekcap.akka.prime.message.Result; public class PrimeListener extends UntypedActor { @Override public void onReceive( Object message ) throws Exception { if( message instanceof Result ) { Result result = ( Result )message; System.out.println( "Results: " ); for( Long value : result.getResults() ) { System.out.print( value + ", " ); } System.out.println(); // Exit getContext().system().shutdown(); } else { unhandled( message ); } } } The PrimeListener handles Result messages that contain the final results of all PrimeWorker processing. It handles this message by printing all of the values in the Results list, then it shuts down the entire actor system by retrieving its actor context and then invoking the actor system’s shutdown() method. This shutdown step is not required if you have additional work to perform. In this example we’ve reached the end of the work for the application, so we shut down the system so that the application can exit. PrimeCalculator Finally, Listing 9 shows the PrimeCalculator, which creates the ActorSystem, configures the PrimeMaster and PrimeListener, and then submits work to the PrimeMaster. Listing 9. PrimeCalculator.java package com.geekcap.akka.prime; import akka.actor.*; import com.geekcap.akka.prime.message.NumberRangeMessage; public class PrimeCalculator { public void calculate( long startNumber, long endNumber ) { // Create our ActorSystem, which owns and configures the classes ActorSystem actorSystem = ActorSystem.create( "primeCalculator" ); // Create our listener final ActorRef primeListener = actorSystem.actorOf( new Props( PrimeListener.class ), "primeListener" ); // Create the PrimeMaster: we need to define an UntypedActorFactory so that we can control // how PrimeMaster instances are created (pass in the number of workers and listener reference ActorRef primeMaster = actorSystem.actorOf( new Props( new UntypedActorFactory() { public UntypedActor create() { return new PrimeMaster( 10, primeListener ); } }), "primeMaster" ); // Start the calculation primeMaster.tell( new NumberRangeMessage( startNumber, endNumber ) ); } public static void main( String[] args ) { if( args.length < 2 ) { System.out.println( "Usage: java com.geekcap.akka.prime.PrimeCalculator <start-number> <end-number>" ); System.exit( 0 ); } long startNumber = Long.parseLong( args[ 0 ] ); long endNumber = Long.parseLong( args[ 1 ] ); PrimeCalculator primeCalculator = new PrimeCalculator(); primeCalculator.calculate( startNumber, endNumber ); } } The PrimeCalculator sets up the ActorSystem, the PrimeListener, and the PrimeMaster in its calculate() method. Note that actors live in their own hierarchies, so the PrimeCalculator sets up the PrimeMaster and then the PrimeMaster sets up all of its PrimeWorkers. The additional complexity in the PrimeMaster ActorRef creation is that we need to pass parameters to the PrimeMaster‘s constructor, namely the number of workers to use and a reference to the listener to notify when it is complete. To do this, we create an anonymous inner class that extends the UntypedActorFactory and overrides its create() method. This allows us to create and optionally configure the PrimeMaster before it is wrapped by an ActorRef and returned to the caller. Finally we start the process by invoking the PrimeMaster ActorRef‘s tell() method, passing it the number range received from the command line. A Maven build for PrimeCalculator Listing 10 shows the Maven POM file that builds this project. Note that it is the same as the POM file shown in Listing 1, but the mainClass now references PrimeCalculator. Listing 10. pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.geekcap.akka</groupId> <artifactId>AkkaSample</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>AkkaSample</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.geekcap.akka.prime.PrimeCalculator</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>install</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-actor_2.10</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </project> You can build the project with the following command: mvn clean install You can execute it from the target directory as follows: java -jar AkkaSample-1.0-SNAPSHOT.jar 1 23 Passing “1 23” to the PrimeCalculator tells it to return all prime numbers between 1 and 23. The output should resemble the following: Number Rage: 1 to 2 Number Rage: 3 to 4 Number Rage: 5 to 6 Number Rage: 11 to 12 Number Rage: 13 to 14 Number Rage: 15 to 16 Number Rage: 17 to 18 Number Rage: 19 to 23 Number Rage: 9 to 10 Number Rage: 7 to 8 Results: 1, 2, 3, 5, 11, 13, 17, 19, 23, 7, I left the number-range display so that you can see the numbers that each worker is processing. Because processing is asynchronous, the numbers are not in order. This is just an artifact of storing data in a list. As an exercise, try fixing this by changing the List to a TreeSet. In conclusion Any application of substance will eventually face concurrency issues; basically, “How can I accomplish all of my work at the same time?” The standard implementation is to manually create threads and then synchronize access to shared objects, in order to ensure that data is not corrupted. This is technically challenging and can lead to problems such as thread depravation, thread deadlock, and scalability issues. Akka resolves these familiar concurrency problems in a different way by implementing the actor model design pattern. In the actor model, all work units are defined as actors and communication between them is accomplished via message passing. Akka handles all of the underlying threading complexity leaving you to divide your tasks into actors, define messages to pass between actors, and wire together their communication logic. Akka simplifies concurrent code, but more importantly, it provides infrastructure that allows you to scale without changing your application: if necessary, you could start up hundreds of Akka servers to run your actors. Akka seamlessly handles the distribution of messages and communication between actors. Being able to distribute actors across multiple machines is the real power of Akka, especially for systems where high scalability is a requirement. Steven Haines is a technical architect at Kit Digital, currently working onsite at Disney in Orlando. He is the founder of www.geekcap.com, an online education website, and has written hundreds of Java-related articles as well as three books: Java 2 From Scratch, Java 2 Primer Plus, and Pro Java EE Performance Management and Optimization. He lives with his wife and two children in Apopka, Florida. Open SourceWeb DevelopmentJavaSoftware DevelopmentDevelopment ToolsScala