He describes concurrency as an "iceberg" subject:
Concurrency in Java is a very difficult, iceberg subject, and I guarantee your intution will fail you more often than not.
And he's more right than he realizes! At one point we were going to put an iceberg on the cover, and the introduction was even titled "Under the waterline" (though the metaphor does not appear in the final version of the book at all.) But then we realized that leading with something so depressing might not be the best strategy.
From an earlier draft of the introduction:
Icebergs typically have only ten to twenty percent of their volume above the waterline ??? the rest is hidden from view, lurking underwater. Concurrency is sort of like an iceberg ??? the explicit elements (synchronization, notification) are above the waterline and are clearly labeled, but in order to avoid an unpleasant collision, you must also be aware of what is below the waterline ??? and there???s a lot down there. Below the waterline lies all of the implicit policies and agreements that govern how these mechanisms are intended be used and mean in specific situations within your program. While it may sound trivial, it is not enough to simply use synchronization to ensure thread safety ??? you have to use synchronization correctly and consistently. Describing how to craft and document sensible under-the-waterline policies for the effective use of Java???s built-in concurrency constructs is the central goal of this book. Fortunately, it is not prohibitively difficult to develop a strategy that encompasses the submerged elements ??? as long as you take them into account from the beginning.
All concurrency constructs (such as synchronization, notification, or interruption) involve more than one thread. One of the reasons that writing and maintaining concurrent code in Java is so difficult is that all of the major concurrency constructs involve some degree of implicit agreement between multiple parties as to how the concurrency construct is to be used or interpreted . The concurrency constructs provided by the language are low-level mechanisms, but they are used in applications to enforce application-level concurrency policies or protocols. The tricky part is that policies are defined by the developer and can vary across programs or classes; the same mechanism can be used to enforce a variety of policies. These policies are not captured anywhere in the code (if you???re lucky, it???s captured in documentation, but more often it is not), and therefore it is difficult to automatically (or manually) verify or validate that a program is using the concurrency mechanisms correctly.
Each of the mechanisms for concurrency ??? such as synchronization, notification, and interruption ??? involves cooperation between two or more threads, and each involves an implicit and explicit component. With synchronization, the act of taking and releasing the lock is explicit, but the reason for taking and releasing the lock ??? because the lock guards a particular set of shared state variables ??? is implicit. Similarly, the act of waiting or notifying on a condition variable is explicit, but why you are waiting ??? that you are waiting for a specific condition predicate to become true ??? is implicit.