aws partner logo
ibm partner logo
googlecloud partner logo
microsoft partner logo
Partners
Blogs

Actors in Action – Sleeping Barber Problem with AKKA

18/Mar/2013 Posted By sammer no comments.
Tags

In my last post I talked about the basic concepts and an introduction to the Actors model for concurrency, let's put Actors into action and try to solve a multithreaded producer consumer problem using Akka. The problem I have picked for this post is commonly known as the Sleeping Barber Problem. Let me give a brief statement of the problem for which we will develop the algorithm.

Problem Statement:

"The sleeping barber problem is a classic inter-process communication and synchronization problem between multiple operating system processes. The problem is analogous to that of keeping a barber working when there are customers, resting when there are none and doing so in an orderly manner.

The barber has one barber chair and a waiting room with a number of chairs in it. When the barber finishes cutting a customer's hair, he dismisses the customer and then goes to the waiting room to see if there are other customers waiting. If there are, he brings one of them back to the chair and cuts his hair. If there are no other customers waiting, he returns to his chair and sleeps in it."

I'll start off by writing an implementation in Java using the standard concurrency constructs available in Java 5+. Let's talk about the domain model of the application first. Barber is the centric entity in this problem, so lets think of Barber as a Runnable which maintains a Shop and has a waiting room which is like a queue of customers.


public class Barber implements Runnable { Queuecustomers; private Shop shop; public Barber(Shop shop) { this.shop = shop; customers = new LinkedBlockingQueue & lt; Customer & gt; (); } public void addCustomerToQueue(Customer customer) { customers.add(customer); } public void run() { while (!customers.isEmpty()) { Customer customer = customers.remove(); performHaircut(customer); shop.releaseChair(); } } private void performHaircut(Customer customer) { System.out.println("Customer " + customer.getId() + " has got a haircut"); }
}

Barber maintains a Queue of customers which represents the waiting room with waiting chairs, whenever a customer arrives it gets added to the Queue. run method in the Barber class is something which is invoked periodically and picks customers from queue, assign it the barber chair and then after performing haircut, releases the chair for the next customer to acquire. Lets see the implementation of the Shop class


public class Shop { private Barber barber; private Semaphore semaphore; private AtomicInteger count = new AtomicInteger(0); public Shop() { semaphore = new Semaphore(3); } public void init() { barber = new Barber(this); Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(barber, 100, 1000, TimeUnit.MILLISECONDS); } public void acquireChair(Customer customer) throws InterruptedException { boolean acquired = semaphore.tryAcquire(1, 100, TimeUnit.MILLISECONDS); if (acquired) { count.getAndIncrement(); barber.addCustomerToQueue(customer); return; } System.out.println("Turning customer " + customer.getId() + " away"); } public void releaseChair() { semaphore.release(1); } public int getSuccessfulHaircutCount() { return count.intValue(); }
}

Shop at the time of initialization passes itself as a callback to the Barber so that all the activities performed by Barber could be recorded by the shop. As you can see above the Shop in the init method, starts barber thread to periodically invoke run method. When a customer arrives ,it tries to acquire a chair which is represented by a Semaphore(3), 3 is the number of spots in the waiting room of the shop and no more than 3 customers can wait at any given point. since customers can arrive in a random and concurrent manner, these chairs are guarded by a Lock provided by the semaphore.

Now, let's try and analyze the implementation and figure out the problems and complexities. The first problem is that the design is not so intuitive as Shop is passed to Barber as a callback and Barber maintains a reference to the Shop, which doesn't sound right if we try to stick to the philosophy of Objects representing real-world entities.

Another problem with the usage of semaphores or Locks is that there is a section of code which would be accessible to only one thread at any given moment , thus we have a point in code which would adversely affect the throughput of the application. There is a chance of deadlock, if we don't put a timeout based tryAcquire, one thread could starve for a resource forever if a minor change in code replaces tryAcquire with Acquire. Now, lets take an alternative approach with the Actor based implementation of the same problem and see what the advantages are. We will start with the domain model, first Entity is the Barber. In this implementation we will treat Barber as an Actor, which reacts at the receipt of some message in the form of a Customer.


public class Barber extends UntypedActor { Random random = new Random(); @Override public void onReceive(Object message) throws Exception { if (message instanceof Customer) { Customer customer = (Customer) message; Thread.sleep(random.nextInt(1150)); System.out.println("Customer " + customer.getId() + " Got a Haircut"); } }
}

Barber receives the Customer and performs the haircut. Lets see now, how the Shop would look like


public class Shop extends UntypedActor { Queuecustomers=new LinkedBlockingQueue(); ActorRef barber; int counter = 0; public Shop(ActorRef barber) { this.barber = barber; } @Override public void onReceive(Object message) throws Exception { if (message instanceof Customer) { processCustomer(message); } else if (message instanceof WakeUp) { barber.tell(customers.poll()); } } private void processCustomer(Object message) { if (customers.size() == 3) { Customer customer = (Customer) message; System.out.println("Customer " + customer.getId() + " is leaving"); return; } counter++; customers.add((Customer) message); } @Override public void postStop() { System.out.println(counter + " Customers got a haircut today"); super.postStop(); }
}

Here Shop maintains a queue and keeps sending message to the barber, it either sends a customer to the barber or a wake-up message, on which Barber reacts and performs certain activities.  This design does not include any usage of Locks or semaphores, thus removing the possibility of any deadlocks or race conditions. Another benefit of this design is that its much simpler as compared to the one suited for Shared memory model of Java. Here Actors send immutable message to the other actors and the communication is inherently thread safe. We can expect higher throughput as there are no synchronized blocks required on any critical section of the code which are guarded by locks. There is no possibility of one Actor starving on the availability of a shared resource. After having talked about all the above factors comes the biggest benefit of this approach is the lack of non-determinism, you can simply unit test the Actor classes and be sure that there is no unpredictable behavior in another environment. We finally need a class which would manage the Actors and start sending messages to them, lets see how it looks like


import akka.kernel.Bootable;
public class BarberShopServer implements Bootable { private ActorSystem system; private ActorRef shop; private ActorRef barber; public void shutdown() { System.out.println("Shutting Down shop for the day"); } @SuppressWarnings("serial") public void startup() { System.out.println("Opening up Shop for the Day"); system = ActorSystem.create("BarberShopApp", ConfigFactory.load() .getConfig("BarberShopApp")); barber = system.actorOf(new Props(Barber.class)); System.out.println("Barber Walks in ...."); shop = system.actorOf(new Props(new UntypedActorFactory() { public UntypedActor create() { return new Shop(barber); } }), "shop"); System.out.println("Shop is open..."); }
}

Bootable class is like the startup class for any deployed Akka application. To run the application simply build the jar file copy it to the $AKKA_HOME/deploy folder and typing Akka BarberShopServer, would start the application. You can send messages to the deployed Actors using following code


final ActorRef shop=system.actorOf(new Props(new UntypedActorFactory(){ public UntypedActor create(){ return new Shop(barber); }
}),"shop");
shop.tell(new Customer())

I'll soon be uploading the entire code for both Core Java and Akka implementations to github and update the post with the same. I believe this post gives a basic idea of how we can go about designing applications challenged concurrently using Actors in a much simpler and intuitive way. Any questions and comments are welcome.

Category : App Development