Recently I have been thinking hard about threading in a Java project of mine, and it definitely requires a great deal of hard thinking. When I started working with Java there weren't any native threads, so it was just easier to make everything single threaded as the performance gain was minimal. However, as time has passed more and more of my code both at work and home has needed to work multi-threaded. As I learn more about Java threading, I realise how wrong I was doing it before - a cycle repeated a few times in the last 12 years. It was only after reading the great book Java Concurrency In Practice a few years ago that I realised that in Java "locking is not just about mutual exclusion; it is also about memory visibility." I can only hope that this is the last such cycle, but realise it's probably not.
- Talking to other developers there seems to be a standard progression in Java multi-threaded programming skill:
- Just write single threaded programs
- Write multi-threaded programs by marking everything synchronized
- Use wait() and notifyAll() to build up "threadsafe" collection classes
- Realise your "thread-safe" collection classes aren't particularly thread-safe and try using immutable domain objects with stateless controllers to work around the problem
- Trust in the Java API concurrent utilities, with its collections, locks and executors
- Realise that these classes don't solve all the problems and start thinking hard about all the issues in your code
- Become very scared about Java threading
I think the next step may be using another language that doesn't allow shared state. Scala uses an Actor model to asynchronously pass messages between threads. There is no shared memory so no threading problems. This sounds like a good way to go, but I still work in Java for most of my programming (not many Scala or Erlang jobs around!).
This whole situation reminds me of memory management in C++. There were simple rules to follow to reduce memory leakage, but the problem was hard to completely solve (especially to a newbie programmer) and normally your program still leaked. I remember leaning heavily on Purify. Then Java came along with its automatic memory management and the problem largely disappeared. Developers could focus on the business logic of their code rather than housekeeping. Hopefully a similar thing will happen with Java threading.