The title of this essay is a bit tongue-in-cheek because, no matter how promising the new stalwart JVM languages (And here I'm thinking of the most promising of those languages, viz. Scala and Clojure) are with helping us manage concurrency, the fact is that Java is going to be around in a big way (for a long time, and that's my two cents' worth). In fact, the title of this essay (i.e. Java Concurrency in Retrospect) is in part a tribute to, and a spin on, the title of the amazingly rich and highly regarded book Java Concurrency in Practice, by Brian Goetz, et al.
The book Java Concurrency in Practice is so well-established as staple reading material that it's commonly and affectionately referred to simply as JCIP, somewhat akin to how the Gang of Four (the authors of the seminal work ...) are endearingly called the GoF (sans the late John Vlissides, one of the most terrific writers to ever have graced our software industry's literature, thinking here especially to Design Patterns:Elements of Reusable Object-Oriented Software as well as to Pattern Hatching: Design Patterns Applied). Adding that JCIP is evidently also esteemed in academia; a friend of mine, who is an algorithms aficionado as well as a professor of computer science at The University of Texas at Austin, thinks highly of it. Another Austin-based professional that I know, a web applications architect who helps start-ups and early stage companies implement high performance, fault tolerant platforms, told me that he recommends to everyone that they read JCIP in its entirely, every six months. These are anecdotes, but I think that you get the idea :-)
Bruce Tate, the author of Beyond Java, has probably the most germane and succinct round-up of exactly what it is that makes JCIP so compelling. Tate had noted the following thoughts upon its publication, and his words ring true today:
I've focused a career on simplifying simple problems, but this book ambitiously and effectively works to simplify a complex but critical subject: concurrency. Java Concurrency in Practice is revolutionary in its approach, smooth and easy in style, and timely in its delivery—it's destined to be a very important book.
And there is no hand-waving going on in the pages of JCIP. No fluff, all stuff, as they say! There are tons of Java code listings peppered throughout the book, along with clear explanations. Here's a small sample of the types of listings you can expect to find in it:
1.1 Non-thread-safe sequence generator1.2 Thread-safe sequence generator2.1 A stateless servlet2.2 Servlet that counts requests without the necessary synchronization. Don't do this.2.3 Race condition in lazy initialization. Don't do this.2.4 Servlet that counts requests using AtomicLong2.5 Servlet that attempts to cache its last result without adequate atomicity. Don't do this.2.6 Servlet that caches last result, but with unnacceptably poor concurrency. Don't do this.2.7 Code that would deadlock if intrinsic locks were not re-entrant2.8 Servlet that caches its last request and result3.1 Sharing variables without synchronization. Don't do this.3.2 Non-thread-safe mutable integer holder3.3 Thread-safe mutable integer holder3.4 Counting sheep3.5 Publishing an object......
Now, the polyglot JVM languages (Again, I'm thinking here of the eminently promising languages Scala and Clojure) offer compelling solutions to solving concurrency concerns in corporate software development. In referring to Paul Graham’s essay Beating the Averages, which is an interesting look at the inner workings of his company Viaweb during the years prior to being bought by Yahoo! Inc. in 1998, authors Michael Fogus and Chris Houser note in their fantastic book The Joy of Clojure: Thinking the Clojure Way (Manning Publications) that:
Clojure is a dialect of Lisp directly supporting concurrent software development using functional programming techniques, and like the Lisp described in Beating the Averages, provides an environment conducive to agility. Clojure fosters agility in ways that many popular programming languages can’t. Many programming languages are bewitched with most or all of the following:
- Unavoidable boilerplate
- A long thought-code-feedback loop
- Incidental complexity
- Difficulties in extension
- Deficiencies in supporting crucial programming paradigms
The author Nilanjan Raychaudhuri adds some highly relevant observations of his own, in his book Scala in Action (Manning Publications), when he notes that:
- Only a handful of programmers know how to write a correct, concurrent application or program. The correctness of the program is important.
- Debugging multithreaded programs is difficult. The same program that causes deadlock in production might not have any locking issues when debugging locally. Sometimes threading issues show up after years of running in production.
- Threading encourages shared state concurrency, and it’s hard to make programs run in parallel because of locks, semaphores, and dependencies between threads.
In particular, Clojure provides the Software Transactional Memory model to tame concurrency concerns. Again, this is Fogus and Houser from their previously cited work:
On one side of the equation (italics are mine), Clojure utilizes Software Transactional Memory (STM), agents, a clear distinction between identity and value types, arbitrary polymorphism, and functional programming to provide an environment conducive to making sense of state in general, and especially in the face of concurrency. On the other side, Clojure shares a symbiotic relationship with the Java Virtual Machine, thus allowing prospective developers to avoid the costs of maintaining yet another infrastructure while leveraging existing libraries.
Raychaudhuri notes further in his excellent book Scala in Action (Manning Publications) the solution that Scala brings to the table when he notes that:
...The current concurrency model is too hard for developers to grok, and we need a better concurrent programming model that will help developers easily write and maintain concurrent programs... Scala takes a totally different approach to concurrency: the Actor model. An actor is a mathematical model of concurrent computation that encapsulates data, code, and its own thread of control and communicates asynchronously using immutable (no side effects) message-passing techniques...
Compelling as the solutions are for the JVM, the one from Clojure and the one from Scala, both compelling in their own right and each worthy of investigation, we meanwhile have massive Java code-bases already in existence, so JCIP will continue to light the way in helping us developers tame the beast that is concurrency.
During my daily programming, although I may not necessarily be thinking explicitly in terms of concurrency concerns all the time, the theme of handling concurrency, nonetheless, forms a fairly consistent backdrop. In conclusion, I should point out yet another terrific online resource, not necessarily concurrency-focused, by the lead author of JCIP, Brian Goetz.
That link will take you to the excellent Java theory and practice Technical Library hosted under the auspices of IBM developerworks. The articles by Goetz are of the highest quality; I'm talking about as polished as the finest book you'll find, right up there with Design Patterns:Elements of Reusable Object-Oriented Software, or Effective Java by Joshua Bloch. Actually, I had been searching during this past year for helpful material on grokking proxies and their use in decorating messaging in framework code, and that's how I came across the Java theory and practice Technical Library. And I sure am glad I did :-) Here's a small sample of the kind of articles you can expect to find there:
- To mutate or not to mutate?
- Decorating with dynamic proxies
- State replication in the Web tier
- Are all stateful Web applications broken?
- Introduction to non-blocking algorithms
- Instrumenting applications with JMX
Here's to continually improving our software code-bases through refactoring. Did someone say refactoring? Now that sound like the topic for another essay...