Last time we covered the basics of HashMap. For those of you who haven’t seen it, click on the link below:
Comics: What is a HashMap?
In this installment, we’ll take a look at the potentially fatal problems with HashMaps in high-concurrency environments.
The capacity of HashMap is limited. When the HashMap reaches a certain saturation after multiple element inserts, the probability of conflicts in Key mapping positions increases gradually.
At this point, the HashMap needs to expand its length, which is Resize.
There are two factors that affect the occurrence of Resize:
1.Capacity
The current length of the HashMap. As I mentioned in the last installment, the length of a HashMap is a power of two.
2.LoadFactor
HashMap load factor, default is 0.75F.
The criteria for measuring whether a HashMap is resized are as follows:
HashMap.Size >= Capacity * LoadFactor
Expansion and 1.
Creates a new empty array of entries twice as long as the original array.
2.ReHash
Iterating through the original Entry array, rehash all the entries into the new array. Why rehash it? As the length increases, the Hash rules change.
Let’s review the Hash formula:
Index = HashCode (Key) & (length-1)
If the array length is 8, the Hash operation is performed with 111B. The new array is 16 in length and Hash with 1111B. The Hash results are obviously different.
HashMap before Resize:
HashMap after Resize:
The Java code for ReHash is as follows:
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null ! = e) { Entry<K,V> next = e.next;if (rehash) { e.hash = null == e.key ? Zero:hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; }}}Copy the code
Note: The following content is very brain-burning, please sit firmly and hold well.
Suppose a HashMap has reached the critical point of Resize. Two threads, A and B, Put the HashMap at the same time:
When the Resize condition is reached, the two threads perform the first step of Rezie expansion respectively:
At this point, both threads have reached the ReHash step. Let’s review the code for ReHash:
If thread B is traversing the Entry3 object, the thread is suspended as soon as it completes the line in the red box. For thread B:
e = Entry3
next = Entry2
Thread A is rehashing unimpeded, and when the Rehash is complete, the result looks like this (e and Next in the figure, representing two references to thread B) :
Up until this point, everything looks fine. Thread B then resumes and continues with its own ReHash. Thread B was in the following state:
e = Entry3
next = Entry2
When we get to the above line, obviously I = 3, because thread A’s hash for Entry3 is also 3.
We continue to the two lines where Entry3 is placed at subscript 3 in thread B’s array, and e points to Entry2. Now e and next point to the following:
e = Entry2
next = Entry2
The overall situation is shown in the figure:
The next loop executes to the line in the red box:
e = Entry2
next = Entry3
The overall situation is shown in the figure:
Insert Entry2 into the head of thread B’s array using the following three lines:
The overall situation is shown in the figure:
The third loop starts with the code in the red box:
e = Entry3
next = Entry3.next = null
In the final step, the miracle comes when we execute the following line:
newTable[i] = Entry2
e = Entry3
Entry2.next = Entry3
Entry3.next = Entry2
The linked list has rings!
The overall situation is shown in the figure:
At this point, the problem hasn’t arisen directly. When Get is called to find a nonexistent Key and the Hash value of the Key is equal to 3, the program will go into an infinite loop because position 3 has a circular list!
This situation can not help but remind people of a classic interview question:
Comics algorithm: How to determine the linked list has a ring?
1. A Hashmap needs to be resized when too many elements are inserted
HashMap.Size >= Capacity * LOadFactor.
2.The HashmapResize consists of two steps: expansion and ReHash. ReHash may form a linked list ring in the case of concurrent operations.
— — the END — — –
If you like this article, please long click the picture below to follow the subscription number programmer Xiao Grey and watch more exciting content