HashMap为什么是线程不安全的
来源:互联网 发布:mysql判断崩溃 编辑:程序博客网 时间:2024/05/15 23:49
addEntry RemoveEntry reszie 三个函数这里会出问题,简而言之就是在获取hashmap的链表头这个资源容易出现问题。对其的增删改会不支持多线程访问。
1、
void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); }
在hashmap做put操作的时候会调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失
2、
final Entry<K,V> removeEntryForKey(Object key) { int hash = (key == null) ? 0 : hash(key.hashCode()); int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; }
当多个线程同时操作同一个数组位置的时候,也都会先取得现在状态下该位置存储的头结点,然后各自去进行计算操作,之后再把结果写会到该数组位置去,其实写回的时候可能其他的线程已经就把这个位置给修改过了,就会覆盖其他线程的修改
3、addEntry中当加入新的键值对后键值对总数量超过门限值的时候会调用一个resize操作,代码如下:
HashMap resize 方法解释void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;// 这个if块表明,如果容量已经到达允许的最大值,即MAXIMUN_CAPACITY,则不再拓展容量,而将装载拓展的界限值设为计算机允许的最大值。// 不会再触发resize方法,而是不断的向map中添加内容,即table数组中的链表可以不断变长,但数组长度不再改变if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}// 创建新数组,容量为指定的容量Entry[] newTable = new Entry[newCapacity];transfer(newTable);table = newTable;// 设置下一次需要调整数组大小的界限threshold = (int)(newCapacity * loadFactor);}
这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。
当多个线程同时检测到总数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。HashMap transfer方法实现void transfer(Entry[] newTable) {// 保留原数组的引用到src中,Entry[] src = table;// 新容量使新数组的长度int newCapacity = newTable.length; // 遍历原数组for (int j = 0; j < src.length; j++) {// 获取元素eEntry<K,V> e = src[j];if (e != null) {// 将原数组中的元素置为nullsrc[j] = null;// 遍历原数组中j位置指向的链表do {Entry<K,V> next = e.next;// 根据新的容量计算e在新数组中的位置int i = indexFor(e.hash, newCapacity);// 将e插入到newTable[i]指向的链表的头部e.next = newTable[i];newTable[i] = e;e = next;} while (e != null);}}}
0 1
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的?
- 为什么HashMap是线程不安全的
- HashMap为什么是线程不安全的?
- HashMap为什么是线程不安全的
- HashMap为什么是线程不安全的?
- 为什么HashMap是线程不安全类?
- hashmap为什么线程不安全
- HashMap为什么线程不安全
- android Studio将String.xml中的字段导出。
- iOS原生Http请求,get post 异步
- Arduino 和LCD1602液晶屏 I2C接口实验
- jquery ajax监听 then when done
- Alfred 爬坑
- HashMap为什么是线程不安全的
- pyhton 中的字符串切片问题
- Button英文字符自动大写的问题
- poj1050
- 自定义UITableView,实现cell的三级分组展开收起
- 移动端开发调试工具,别点,就两个链接而已
- PDF转Word转换器
- 我的struts2学习之一
- leetcode #64 in cpp