Know the Unknown
HashMap is fast but not thread-safe. ConcurrentHashMap is thread-safe. It is fast too, because retrieval methods are not locked and there is no locking of entire map. This means multiple threads can read the map safely without locking and contension.
In general, locking in ConcurrentHashMap fine grained unlike the coarse synchronization of all the methods in Hashtable. Look at size() method, for example, where it first checks the map size without locking. Only if those initial checks fail, map is locked to calculate the size.
Unlike Hashtable, lookup(get()) methods in ConcurrentHashMap are not synchronized. So size() and get() may not be fully accurate. For example, if a PutAll is going on by one thread and another thread attempts size(), then size() may return before putAll() finishes. The value returned by size() in this case would be different from what it would be after putAll finishes. Similarly, ConcurrentHashMap takes a snapshot of entries for iterators while allowing other threads to insert or delete entries. So iterator may not reflect the accurate current state of the map. Application will have to implement custom synchronization if accurate iterator is required.
Hashtable and SynchronizedHashMap use a single monitor to synchronize all the methods. This essentially gives access to only one thread to the entire map(both read and write methods) and blocks all the other threads. This will cause contension. Hashtable and SynchronizedMap are almost similar. However, Hashtable returns the old Enumeration iterator while SynchronizedHashMap returns Iterator, which fails-fast on concurrent modification.