1. Core Conclusion
JDK 1.8 HashMap only resolves the circular linked list infinite loop problem in JDK 1.7 during concurrent resizing by adopting the tail insertion method. However, it fails to fix the data loss issue caused by concurrent resizing. HashMap is still not thread-safe, and concurrent resizing by multiple threads will still lead to element loss and data overwriting.
2. Scenario Prerequisites
- The initial capacity of the HashMap table array is 2
- The linked list at index 1 of the original table:
3 -> 7 -> 5 - Two concurrent threads (Thread A and Thread B) execute the
resize()method simultaneously - JDK 1.8 resizing logic: The original linked list is split into two sub-linked lists based on the high-order bit of the node hash value. The low-order linked list remains at the original index, while the high-order linked list is migrated to the position of
original index + original capacity. Nodes are migrated via the tail insertion method.
3. Reproduction of Data Loss via Thread Scheduling Sequence
Step 1: Both threads enter the resizing logic, and Thread A is blocked midway
Both Thread A and Thread B read the old table array simultaneously and start traversing and migrating nodes of the linked list at index 1.
When Thread A traverses the node e=3 and caches the reference of the next node next=7, a CPU time slice switch occurs, and Thread A is suspended and blocked.
Step 2: Thread B completes full resizing and updates the table array
Without being blocked, Thread B completely traverses the entire linked list 3->7->5 and migrates nodes according to hash rules:
- Element 5: The high-order hash bit is 0, so it remains at index 1 of the new table.
- Elements 3 and 7: The high-order hash bit is 1, so they are migrated to index 3 of the new table, forming the linked list
3->7->null.
Thread B finishes all node migrations and assigns the resized new array to the globaltable variable of HashMap, making the old array reference invalid.
Latest table state in memory:
table[1] = 5table[3] = 3 -> 7 -> null
Step 3: Thread A resumes execution and causes data loss via outdated references
After resuming the CPU time slice, Thread A still holds the outdated reference of the original linked list before resizing and continues the traversal loop:
- Processes the node
e=3and completes its migration. - Assigns the cached
next=7to variableeand continues processing node 7. - Reads the successor pointer of node 7:
7.next = null, and assignsnullto thenextvariable. - The loop terminates directly as
next == null.
Step 4: Final Result – Element 5 is permanently lost
Thread A only traverses and migrates nodes 3 and 7, failing to detect node 5 at all, so no migration operation is performed on element 5.
After Thread A completes migration and overwrites the table array, element 5 no longer exists in the new array, resulting in permanent data loss.