HashMap其实并非线程安全的,在高并发的状况下,会产生并发引发的问题:
好比:java
下面逐个分析下出现上述状况的缘由:安全
HashMap进行存储时,若是size超过(当前最大容量*负载因子)时候会发生resize,首先看一下resize源代码:多线程
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; // transfer方法是真正执行rehash的操做,容易在高并发时发生问题 transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); }
而这段代码中又调用了transfer()方法,而这个方法实现的机制就是将每一个链表转化到新链表,而且链表中的位置发生反转,而这在多线程状况下是很容易形成链表回路,从而发生死循环,咱们看一下他的源代码:并发
void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } }
HashMap死循环演示:
假若有两个线程P一、P2,以及table[]某个节点链表为 a->b->null(a、b是HashMap的Entry节点,保存着Key-Value键值对的值)ide
P1先执行,执行完"Entry<K,V> next = e.next;"代码后,P1发生阻塞或者其余状况再也不执行下去,此时e=a,next=b高并发
transfer(newTable); //P1阻塞在transfer方法中,没有执行到下边对 table 和 threshold 从新赋值的操做 table = newTable; threshold = (int)(newCapacity * loadFactor);
一个线程利用迭代器迭代时,另外一个线程作插入删除操做,形成迭代的fast-fail。ui
public class TestFailFast { private static final String USER_NAME_PREFIX = "User-"; // Key: User Name, Value: User Age private static Map<String, Integer> userMap = new HashMap<>(); // ThreadA 用于向HashMap添加元素 static class ThreadA implements Runnable { @Override public void run() { System.out.println("ThreadA starts to add user."); for (int i = 1; i < 100000; i++) { userMap.put(USER_NAME_PREFIX+i, i%100); } System.out.println("ThreadA done."); } } // ThreadB 用于遍历HashMap中元素输出 static class ThreadB implements Runnable { @Override public void run() { System.out.println("ThreadB starts to iterate."); for (Map.Entry<String, Integer> user : userMap.entrySet()) { System.out.println("UserName=" + user.getKey() + ", UserAge=" + user.getValue()); } System.out.println("ThreadB done."); } } public static void main(String[] args) throws InterruptedException { Thread threadA = new Thread(new ThreadA()); Thread threadB = new Thread(new ThreadB()); threadA.start(); threadB.start(); threadA.join(); threadB.join(); System.exit(0); } }
运行结果:抛出ConcurrentModificationException.net
ThreadA starts to add user. ThreadB starts to iterate. Exception in thread "Thread-1" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437) at java.util.HashMap$EntryIterator.next(HashMap.java:1471) at java.util.HashMap$EntryIterator.next(HashMap.java:1469) at concurrent.TestFailFast$ThreadB.run(TestFailFast.java:33) at java.lang.Thread.run(Thread.java:748) ThreadA done.
HashMap并不是线程安全,因此在多线程状况下,应该首先考虑用ConcurrentHashMap,避免悲剧的发生。线程
https://blog.csdn.net/chenxuegui1234/article/details/39646041
https://blog.csdn.net/u011716215/article/details/78601916code