HashMap为什么是线程不安全的

转载 2016年05月31日 00:54:45

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、


  1. 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++) {
// 获取元素e
Entry<K,V> e = src[j];
if (e != null) {
// 将原数组中的元素置为null
src[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);
}
}
}



hashMap线程不安全的原因及表现

hashMap出现线程不安全的原因: HashMap的实现里没有锁的机制,因此它是线程不安全的。其实只要有锁的机制,可以通过锁实现线程安全,我们在读写HashMap对象的时候加锁,以保障这个对象的线...
  • VIP_WangSai
  • VIP_WangSai
  • 2017年04月15日 10:59
  • 923

HashMap为什么线程不安全以及解决方法

众所周知,hashmap线程不安全而hashtable线程安全。最近在看并发编程就稍微研究了一下。先看一段JAVAAPI中对hashmap的介绍:*注意,此实现不是同步的。如果多个线程同时访问此映射,...
  • Yixiong_Cao
  • Yixiong_Cao
  • 2015年12月10日 19:26
  • 4044

HashMap线程不安全在哪里

线程不安全如果多个线程同时访问这个Map,而且至少一个线程对Map进行结构性的修改(增加,删除操作,update不算),那么它必须在外部进行同步。比如一个线程对HashMap进行扩容,另外一个线程读取...
  • zly9923218
  • zly9923218
  • 2016年05月25日 12:42
  • 1914

证明HashMap是线程不安全的

在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性。在系统运行中,使用到这些缓存数据,都可以直...
  • qq991029781
  • qq991029781
  • 2016年03月19日 11:34
  • 2263

HashMap多线程不安全

事故分析最近一次web工程上线,上线大概半个小时,出现了报警,16核的服务器的cpu使用了1123%,程序出异常了。 Cpu利用率过高一般是因为出现了出现了死循环,导致部分线程一直运行。占用cpu时...
  • u012973218
  • u012973218
  • 2017年05月11日 10:26
  • 215

HashMap为什么线程不安全

一直以来都知道HashMap是线程不安全的,但是到底为什么线程不安全,在多线程操作情况下什么时候线程不安全? 让我们先来了解一下HashMap的底层存储结构,HashMap底层是一个Entry数组,...
  • A_lele123
  • A_lele123
  • 2015年08月14日 15:02
  • 2327

HashMap为什么线程不安全?浅析高并发情况下的HashMap

1.Hashmap在插入元素过多的时候需要进行Resize; 2.Hashmap扩容时Resize的ReHash步骤,并发的情况下可能会形成链表环;...
  • V_Axis
  • V_Axis
  • 2017年11月22日 16:50
  • 271

HashMap的线程不安全体现

为什么都说HashMap是线程不安全的呢?它在多线程环境下,又会发生什么情况呢?resize死循环我们都知道HashMap的初始容量是16,一般来说,当插入数据时,都会检查容量有没有超过设定的thre...
  • jim__charles
  • jim__charles
  • 2016年12月19日 21:09
  • 1187

单例设计模模式——懒汉式为什么线程不安全

单例设计模式分为两种饿汉式,懒汉式无论哪一种,思想就三步,0;就一个实例,则不能被实例化,所有构造函数私有的1:提供一个实例2:提供一个供外部访问的方法 懒汉式线程不安全,那为什么不安全呢?看代码!...
  • L_ILove
  • L_ILove
  • 2017年01月09日 21:39
  • 2374

Servlet为什么是线程不安全的?

"Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。...
  • gjf281
  • gjf281
  • 2014年04月21日 18:30
  • 1090
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HashMap为什么是线程不安全的
举报原因:
原因补充:

(最多只允许输入30个字)