The next big challenge in the modern software development is writing concurrent, distributed and scalable programs. Algorithms that can cut down tasks in parallel and offer a greater throughput. The current shared memory model of Java concurrency offers an excellent API for writing such programs. Java’s concurrency model has evolved over the last couple of releases and has made a programmer’s life much easier dealing with Multi-threaded programming.
Though Java’s current API simplifies the task of concurrent programming, it can’t solve the problem of Locks and Synchronization. Locks are a necessity when it comes to Multitasking in Java since the model is based on is Shared Memory. Shared variables hold a state which is prone to errors and is really difficult to debug if something goes wrong. Programs that use Locking have different behaviors in different environments. Use of Synchronization with Locks and Semaphores lowers the throughput as the access to shared variables must happen serially in order to get consistent results.
Attempts have been made by Programmers to maximize throughput by introducing Locking at a more granular level , e.g ConcurrentHashMap offers a concurrently accessible thread-safe data structure, however looking at the implementation, one realizes that this is way too hard to code.
Actor model makes an attempt at solving the above mentioned problems by offering a Lock-free approach to concurrency and maximizing throughput with “share nothing” model. The actor model is an altogether different programming paradigm for writing concurrent programs which advocate Message Passing between stateless entities, thus completely getting rid of the shared state which is the root cause of uncertainty, lowered throughput, and non-determinism in the concurrent programming.
Actor is a lightweight entity which communicates with other Actors using messages in a stateless manner. The reason why Actors are considered lightweight is that an Actor does not map one to one with an OS thread which a Thread in Java does. An Actor’s actual resource footprint is only memory, an AKKA actor consumes close to 6 bytes of memory, which means that you can have close to ~2.7 million actors per GB of heap, thus ~2.7 million units of work.I’ll briefly talk about AKKA and its programming model in this post later.
You must be wondering by now that if an Actor only consumes memory, how does it react on the receipt of a message, an Actor is managed by a scheduler thread(which does map one to one with an Operating System Thread) running underneath. The scheduler thread can manage multiple actors.
Lets take a simple example of one Scheduler thread running and listening for messages is managing 100 Actors in a round-robin fashion. When a message is passed to an Actor, the Scheduler thread which is managing a set of 100 actors, listens to it and passes it to one of the available actors, and the Actor acts upon the message as per the logic coded by the programmer. The interesting thing to note here is that one scheduler thread is assigning tasks to different actors being managed in a round-robin fashion concurrently without having to make a context-switch, which is considered to be one of the costlier operations from the CPU cycle standpoint. So, multiple actors can act upon multiple tasks concurrently without paying the penalty for a context switch, thus making an Actor lightweight from another perspective(CPU usage).
The benefit of this approach from a programmer’s point of view is that Actor is at a much more abstract level as compared to a Thread. Java Threads are very low level and a day to day programming task should not be worried about low-level implementations. Actors model abstracts the complexity from the job of writing parallel programs, a programmer should be worried about coding the business logic and not the low-level complexities of multitasking.
I’ll briefly touch upon AKKA now, which is an excellent framework built upon the implementation of Actor model for concurrent programming. Like I mentioned earlier an AKKA actor consumes close to 6 bytes of memory. With a GB of the heap, you can have millions of actors, offering massive scalability. It’s a high-performance system which can consume ~50 million msg/sec on a single machine.
AKKA offers a programming API for both Java and Scala. AKKA’s API is pretty clean and concise interface for programming the Actors model on JVM. Following code snippet shows a way of creating an Actor and sending message to it
First thing you need to do is create an Actor system, which could be considered a container that holds all the Actors.
ActorRef worker = actorSystem.actorOf(new Props(WorkerActor.class).withRouter(new RandomRouter(2)), "worker");
you can only acquire a reference to an Actor via the ActorRef interface, on which you send a message using the syntax mentioned below with a.tell(message). We can also attach a Router(which would be the scheduler for the Actors to manage)
worker.tell("hello"); In Scala
worker ! "Hello"
Hope this post was helpful in understanding the idea of using Actor Programming model, in my next post I’ll do a comparative analysis of how we can implement a simple producer-consumer problem in Java’s shared memory vs AKKA’s Actor model.