About Me

I am a computer programmer. I do programming professionally and for a laugh.

Technical stuff on programming, java, DSLs, etc...

Tuesday 30 August 2011

Happy multi-threading

Multi threading is not one of Java's strong suits. Concurrency library is rather too comprehensive and low level. Objects are exposed, synchronisation is manual. It is easy to get it wrong, very hard to test and debug. Also concurrent applications are considered to be sophisticated. So programmers love concurrency, they sprinkle executers, thread pools etc into a perfectly fine application to make it impossible to understand, non-deterministic and full of race conditions.

But when you buy a server these days it comes with 8 cores minimum. So you would like to parallelise the app, utilise the cores. What to do?

The simplest trick for happy multi-threading is to avoid synchronisation and one of the better patterns for avoiding synchronisation is Actors. Some languages like Haskell and Scala support actors built-in and there are various framework for Java, but here is the main idea;

An actor is a self-contained programming entity that you can communicate via messages. Just like the name suggests, you can send an Actor a message and it acts on the message. You can chose to wait the execution of your message or you can keep doing other things, and later send another message to get a status update.

The trick is that the actor should be self-contained, ie. all its data should be exclusive to that actor only. Also, the messages you send to the actor should be commands, ie. objects that contain mostly processing logic. Any data sent in the commands should be immutable so that they require no synchronisation.

With the above, the actor runs in it's own thread, fed by a message queue, running a processing loop. It gets messages of the queue and acts on them. No synchronisation, no weird timing issues, happy days.

The below code is a simple display of the concept. Mind you there is nothing stopping you to share objects between actors and shoot yourself in the foot. Remember the trick is to isolate the actor from the outside world.

public abstract class Actor<T> {
    private final ExecutorService messageQ = Executors.newSingleThreadExecutor();

   /**
   * Send a message to the actor. Returns null or a RuntimeException if an exception
   * occurred during processing.
   */
   public Future<RuntimeException> send(final T message) {
      return messageQ.submit(new Callable<RuntimeException>() {
         @Override
         public RuntimeException call() {
            try {
                onMessage(message);
                return null;
            } catch (Throwable t) {
                return new RuntimeException("Actor threw an exception during message processing", t);
            }
         }
      });
   }
   /**
   * Send a message to the actor.
   */
   public void sendAndWait(final T message) throws RuntimeException {
      RuntimeException exc = null;
      try {
         Future<RuntimeException> send = send(message);
         exc = send.get();
      } catch (InterruptedException e) {
         throw new RuntimeException("Interrupted Exception: " + e, e);
      } catch (ExecutionException e) {
         throw new RuntimeException("Execution Exception: " + e, e);
      }  
      if (exc != null) {
         throw exc;
      }
   }
   /**

   * typical implementation of the onMessage method should use a pattern matcher to determine the
   * type of the message and act on it. Java unfortunately does not support type based pattern matching built in.
   * Command pattern is your friend.
   */
   protected abstract void onMessage(T msg) throws Exception;
}

4 comments:

  1. throws RuntimeException wtf?

    ReplyDelete
  2. What's the difference between MDBs and Actors?

    ReplyDelete
  3. Actors are great. I wrote an app with a serial conversion method and the same one that used many actors to do each step of the converting in a pipeline. As soon as you get your head round using actors, CPU utilisation goes far higher.

    ReplyDelete
  4. @Anonymous:- "throws RuntimeException" really wtf!
    @Anonymous:- MDBs are like actors wrapped in very thick layers of not always useful infrasturcture code.
    @Tim:- Check Scala actors, they are even lighter weight then threads and with scala's wonderful pattern matching great to work with.

    ReplyDelete

Followers