how is it possible that while thread1 is executing the
put()method acquiring the lock on the entire Hashtable, other thread2 can execute thecontainskey()method?
It's not possible. The issue here is not about Hashtable class itself which, as you point out, is a synchronized class so will not be corrupted. The race condition is that thread-A may test for a key that doesn't exist. But before it can do the put, thread-B tests for the same key that still doesn't exist. Then both threads put the same value into the table with one of them overwriting the other. This is the race.
1 if (! hashtable.contains(key)) { 2 hashtable.put(key, value); 3 } To enumerate the race more completely:
- Thread-A calls
hashtable.contains("foo")which returns false on line #1. - Thread-B runs then also calls
hashtable.contains("foo")which also returns false on line #1. - Thread-A calls
hashtable.put("foo", "bar")on line 2. - Thread-B calls
hashtable.put("foo", "baz")on line 2.
If this happens in this order (and it certainly can), then Thread-B will overwrite Thread-A's value for "foo". Hashtable isn't corrupted but the overwrite may not have been expected by the logic of the code. It might also happen that 4 and 3 are reversed so Thread-A would overwrite Thread-B's value for "foo". The nature of a race condition is that thread run order cannot be predicted withoutand to ensure the right logic you need to apply specific locking.
Btw, Hashtable is an old class and has been superseded by ConcurrentHashMap. But the race condition would still exist with ConcurrentHashMap although it does have atomic operations like concurrentMap.putIfAbsent(key, value) for this reason. See ConcurrentMap javadocs.