Lee stated that many of the concurrency problems he sees involve use of Java collections. He said this presentation is on practical issues the audience would be likely to see. He referenced a session from last year's JavaOne (Robust and Scalable Concurrent Programming: Lessons from the Trenches) and said this year's presentation starts from there and delves a little deeper into the patterns.
Lee said it is better to have correctness first and then achieve performance and scalability next. Because problems usually repeat themselves, anti-patterns serve as "crutches" (red flags) for spotting "bad smell."
Lee showed an example where a concurrency issue arose because of the use of lazy initialization. He stated that we often don't need to load these lazily. He showed how to address this with the use of the volatile keyword in situations when the "data is optional and large." Lee mentioned that use of volatile is "not zero cost," but is typically not expensive enough to worry about.
One observation Lee made was that we often have read-heavy functionality with few writers or write-heavy functionality with few readers. He outlined several implementation choices for the case of "many readers, few writers." I really liked his table summarizing "many readers, fewer writers" with type, concurrency, and staleness behavior column headers. I'd like to get a copy of the slides to use that table as a reference.
Lee also stated that the described copy-on-write approach is less useful for Maps because ConcurrentHashMap works well (albeit at the cost of large memory usage). If read performance is desired enough to justify the cost to writes, copy-in-write is great. However, Lee had some caveats for use of copy-on-write: significant write performance degradation, must avoid direct access to underlying reference, and issues of staleness. In short, what I took away from this section is that, for the case of many reads and few writes, synchronize can be used in the simplest/smallest cases, the concurrent collections will support most general cases best, and copy-on-write might be best when certain conditions exist (no applicable concurrent collection, for example).
Lee was unable to spend as much time on the case of many writers with few readers, but he did cite logging as a use case here. He pointed out that in this case, use of synchronize worsens hotly contested writes. ConcurrentHashMap is generally best again, but he also covered the Asynchronous background processor.
Lee reminded us of advice that is commonly given at these types of sessions: don't tune unless necessary. He also recommended the Highly Scalable Java project rather than rolling custom implementations for highly concurrent applications.
[UPDATE (24 September 2010)] As Sangjin states in the first comment on this post, he has made his slides available for reference at http://www.slideshare.net/sjlee0/concurrency-grab-bag-javaone-2010/download. He also states that he has been told that his slides and the recording of his presentation are accessible versus Schedule Builder. These are well worth checking out.