Concurrent Programming in Java, by Doug Lea, was recommended to me by two presenters at the Microsoft PDC 2005 as the best book about concurrent programming for .NET. It is an impressive book, and it seems fairly straightforward to translate the concepts from the Java world to the .NET Framework.
I can’t give this book a completely fair review, because I wasn’t able to finish it. The book is divided into four chapters; my current interest in concurrent programming was only able to get me through the first two. I don’t doubt that I will find additional value in this book the next time I am faced with a difficult concurrency challenge.
I really enjoyed the first two chapters, though, as they provide the language for discussing concurrent programming. For example, section 1.3 describes the “design forces” to consider when designing concurrent software:
- Safety demands that an object is always in a consistent state. Concurrency makes it possible for one thread to interfere with the operation of another thread acting on the same object, possibly resulting in an inconsistent state.
- Liveness demands that operations run to completion. Concurrent programming can permanently halt progress when two threads are waiting for locks held by the other thread, for example.
- Performance wants operations to run in a timely manner. Concurrency support adds overhead that can hurt performance.
- Reusability wants objects and algorithms to be reusable by different parts of the system. Concurrent programming adds a new dimension to the requirements of reusable software; specifically, clients must know to what extent a component is thread-safe.
Chapter 2 is all about keeping objects in a consistent state by ensuring that two threads cannot manipulate the same data at the same time, either by making objects immutable, or preventing objects from being accessed by other threads, or by synchronizing the use of objects with locks. Understanding of these concepts is crucial to writing safely concurrent code.
Section 2.2.7 is particularly interesting if you’re not familiar with the concept of a “memory model.” Basically, modern compilers and processors can rearrange the order in which statements and instructions are executed, as long as the end result is the same. This can cause very surprising behavior when one thread is observing the activity of another thread. The only practical way to ensure that each thread sees what you would expect it to see is to use synchronization; access to shared memory should always be protected by a lock.
I know that the book has more important concepts; perhaps I’ll make another attempt at reading chapter 3 some day. If you’re doing any concurrent programming at all, I highly recommend that you see how far you can get.