Summary
To make the best of reduced clock speeds and multiple cores of modern CPUs, developers increasingly have to write concurrent programs. The trouble, according to Debasish Ghosh, is that Java still doesn't make concurrent programming easy. He points to actor-based concurrency in Scala as a way to make concurrent programs easier to write and scale.
Advertisement
Computer architecture has been changing in recent years, and with that change developers will have to shift to more concurrency-aware programming models. Writing well-behaving concurrent programs in Java is still not easy, according to Debasish Ghosh' recent blog post, Threadless Concurrency on the JVM - Aka Scala Actors:
One of the commonly mentioned fallouts of the new processor architectures is the new face of the applications written on the JVM. In order to take performance advantage from the multiple cores, applications need to be more concurrent, programmers need to find more parallelism within the application domain... Unless you are Brian Goetz or Doug Lea, designing concurrent applications in Java is hard.
The main reason for that difficulty, according to Ghosh, is that Java supports a shared-state concurrency model that relies on synchronizing concurrent threads via concurrency-related objects, such as locks, barriers, or latches.
Support for other concurrent models may have made concurrent programming in Java easier, according to Ghosh. One such model is message passing, which is common in large-scale distributed systems. Message passing does not rely on shared state between threads, and instead uses messages to communicate between threads.
Another model that Ghosh points to in his post is the actor model of concurrency that dates back the 1970s, and is advocated by languages such as Erlang and Scala:
Actor based concurrency in Erlang is highly scalable and offers a coarser level of programing model to the developers... [A] presentation [on Erlang's concurrency] also gives us some interesting figures—an Erlang based Web server supported more than 80,000 sessions while Apache crashed at around 4,000.
The new kid on the block, Scala brings Erlang style actor based concurrency on the JVM. Developers can now design scalable concurrent applications on the JVM using the actor model of Scala which will automatically take advantage of the multicore processors, without programming to the complicated thread model of Java...
Scala actors provide an ideal model for programming in the non-cooperative virtual machine environment. Coupled with the pattern matching capabilities of the Scala language, we can have the full power of Erlang style concurrency on the JVM...
Event based [threadless] actors [in Scala] offer phenomenal scalability when benchmarked against thread-based actors and thread-based concurrency implementations in Java... Scala contains no language support for concurrency beyond the standard thread model offered by the host environment...
To illustrate his point, Ghosh quotes snippets of code from a recent paper [Note: PDF download] by Philipp Haller and Martin Odersky on Actor-based concurrency in Scala:
class Counter extends Actor {
override def run(): unit = loop(0)
def loop(value: int): unit = {
Console.println("Value: " + value)
receive {
case Incr() => loop(value + 1)
case Value(a) => a ! value; loop(value)
case Lock(a) => a ! value
receive { case UnLock(v) => loop(v) }
case _ => loop(value)
}
}
}
...
val counter = new Counter // create a counter actor
counter.start() // start the actor
counter ! Incr() // increment the value by sending the Incr() message
counter ! Value(this) // ask for the value
// and get it printed by waiting on receive
receive { case cvalue => Console.println(cvalue) }
What do you think of the actor-based concurrency model? And what do you think are the biggest challenges with Java's thread-based concurrency model when writing code for modern-multicore CPUs?
> <p>What do you think of the actor-based concurrency model? > And what do you think are the biggest challenges with > Java's thread-based concurrency model when writing code > for modern-multicore CPUs?</p>
It seems like a fairly useful abstract but it's a strange to me. I'm still getting my head around the 'spirit' of Scala.
The biggest problem in Java's threading model is a a lack of understanding of the memory model and how methods execute. People focus too much making classes thread-safe but really it's more effective to use discrete (i.e. Objects that don't escape the thread) to execute a task and return set a result that can then be retrieved. I suppose this is just a form of the Actor model developed in Java.
Lately I've been writing exclusively java.nio based server side stuff. The preasures to decouple threads from parallel tasks gradually led me to use the classical producer/consumer model for all aspects of the system. There are tasks and there are queues that hold them. Finally there are worker threads working the queues. Even the socket i/o is based on an event queue scheme. This is the simplest most safe architecture and I beleive it performs well enough compared to more complex approaches that try to remove "thread switches" from the path of the data to boost performance. It shure has been much more fun to program the system in this way :) However it has also been hard to defend my choice of architecture. Reading about the actors has been a revelation! I can kind of see the hidden actors everywhere in my ubiquitous producer/consumer architecture. In my scheme however the actors are not directly visibe because each "message" and each "message reaction" are coupled togather into the classical Task object with a "run()" method. However the collection of possible Task classes that can be put on a particular queue in the system can be viewed as an actor. I deliberately don't include the worker thread in this aggregation because each Task on the queue mey be executed by a different worker thread. I think this last point neately illustrates how the actor architecture leads to lighweight processes that are decoupled from the OS heavyweight processes that animate them. Also I had to imlpement the Elrang blocking "receive" via IOC by keeping the relevant "actor" state into objects on the heap. The computation is executed as multiple "hickups" - every time the "actor" does some stuff, mutates the state object, and subscribes for the next event/message. I can see now that there is a way to express this much more neately in terms of actors and "receive". I also realize that what has forced me to do IOC is that the blocking "receive" requires me to store the intermediate state of the computation in a Thread's context - exactly the thing I try to avoid. Again decoupling Threads from actors solves the problem in a cleaner way. Indeed the guys from Scala seem to have done exectly this decoupling with their high-performance actor library. The conclusion from all this seems to be that if pople are educated about actors they will discover them stamped all over known java high-performance/scalability systems. Bringing actors to the java platform mey turn out to be the way fo the future :)