天天看點

HashMap多線程死循環問題

正如上篇文中所說,HashMap不是線程安全的,在被多線程共享操作時,會有問題,具體什麼問題呢,一直沒有個清晰的了解,今天寫了個測試程式調了一下,才明白其中道理。

主要是多線程同時put時,如果同時觸發了rehash操作,會導緻HashMap中的連結清單中出現循環節點,進而使得後面get的時候,會死循環。【關于什麼是rehash,讀者可以自行去google了】

本文主要參考了:http://coolshell.cn/articles/9606.html,測試資料也一樣。

測試代碼:

import java.util.HashMap;

public class HashMapInfiniteLoop {
	
	private static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(2,0.75f);
	public static void main(String[] args) {
		map.put(5, 55);
		
		new Thread("Thread1") {
			public void run() {
				map.put(7, 77);
				System.out.println(map);
			};
		}.start();
		new Thread("Thread2") {
			public void run() {
				map.put(3, 33);
				System.out.println(map);
			};
		}.start();
		
	}

}
           

其中,map初始化為一個長度為2的數組,loadFactor=0.75,threshold=2*0.75=1,也就是說當put第二個key的時候,map就需要進行rehash:

HashMap多線程死循環問題

下面開始調試運作測試程式

(1)首先使Thread1和Thread2都停在run()的第一行:

HashMap多線程死循環問題

此時map的内容為:

HashMap多線程死循環問題

(2)Thread1調試進入JDK源代碼,停在HashMap.transfer()方法内第一行,切換到Thread2,也停在HashMap.transfer()方法内第一行:

HashMap多線程死循環問題

此時map的内容為:

HashMap多線程死循環問題

(3)切換到Thread1, 停在HashMap.transfer()方法内477行【src[j] = null;】;切換到Thread2,也停在477行

HashMap多線程死循環問題

(4)切換回Thread1,繼續執行,停在圖中480行:【int i = indexFor(e.hash, newCapacity);】

HashMap多線程死循環問題

此時map内容沒變,隻是添加了代碼中的e、next指針:

HashMap多線程死循環問題
HashMap多線程死循環問題

(5)切換回Thread2,直接把Thread2執行完畢:

HashMap多線程死循環問題

此時map的内容為:

HashMap多線程死循環問題

(6)切換回Thread1,回到transfer()方法中:

HashMap多線程死循環問題

此時從調試資訊中還可以清晰地看出map的内容:{5=55, 7=77, 3=33}

此時map形如:

HashMap多線程死循環問題

(7)繼續Thread1中的do-while循環,第一次循環過後,依舊【停在480行】:

HashMap多線程死循環問題

(8)第二次循環過後,依舊【停在480行】:

HashMap多線程死循環問題

(8)第二次循環過後,e已經為null,跳出循環:

HashMap多線程死循環問題

(9)離開transfer()方法,回到resize方法中,将newTable指派給map.table,此時在檢視map,預設的toString()方法已經在死循環了

HashMap多線程死循環問題

至此,map中數組索引位置為3的連結清單上已經成功的出現了環,再對map做索引位置為3的get操作,就會死循環在這裡,CPU成功到達100%。

比如,調用map.get(11)時,即會引起死循環。

而且,map中還丢失了元素,(5,55)已經不再map中了。