I was being a bit loose with my terminology. A lot of the methods, like get(...) are synchronized with Hashtable. When you call get(...) other threads calling synchronized methods will have to wait for the other thread's operation to finish. If you look at the get(...) method for ConcurrentHashMap it's not synchronized. My understanding is that it has about 16 internal locks it uses to control access to individuals buckets. That way you improve throughput since the get method can be called and accessed by multiple threads simultaneously. Clearer?
Having said all that, I don't see why anyone would use a Hashtable now that Java added the ConcurrentHashMap class. I bet most people still use Hashtable eventhough there's a better option.
Ah, right. I wasn't paying attention. When you said ConcurrentHashMap, I was thinking Collections.synchronizedMap(), which has identical synchronization to Hashtable. And phrases like "lock the whole object" always make me cringe. People unfamiliar with the synchronization keyword tend to make some rather fuzzy and inaccurate assumptions and then use a phrase like that to describe them.
I've never dug into the details, but you're definitely right in the generalities about how CCM works, and part of why it's preferable to Hashtable or synchronizedMap().
Before CCM came along, there was HashMap. It became available in 1.2, around 1998, and is still the default replacement for Hashtable. HM isn't synchronized, so in a multithreaded context, you'd use Collections.synchronizedMap(). The only reason to use Hashtable for the last dozen years is when you have to for interaction with legacy code. People still use it though. I guess maybe it's still being taught in schools, or maybe people have old textbooks or tutorials. Same thing with Vector vs. ArrayList.
Now that CCM is around, I don't think I ever use synchronizedMap() any more, but I do still use HashMap in single-threaded situations. There's no reason to bring in the extra weight of CCM in those cases.