Why Concurrent Programming?
I've often looked at reams of code where the programmers have made huge numbers of methods synchronized. On further examination, many of those methods might not need to be synchronized. The problem with such excessive use of synchronized methods is performance. Unfortunately, bottlenecks often don't appear until the code is operating under a heavy load, by which time the original coders may have left the organization or moved on to other work.
Excessive use of synchronization may lead to code that doesn't scale well. Going in and changing such code can be scary when the authors are long gone. Fortunately, the java.util.concurrent package provides a rich mix of concurrent facilities. These facilities tend to be more lightweight and flexible than either simple synchronization or the use of wait/notify calls.
This article takes a cross-section of some of the classes from the java.util.concurrent package and demonstrates some of what's possible. I think you'll find the classes surprisingly elegant and reasonably easy to understand.