【面试库】--HashMap多线程put后get null ,get 死循环,get数据丢失(167)

来源:互联网 发布:女程序员标准装扮 编辑:程序博客网 时间:2024/05/20 22:02

0 公共put源码

public  V put(K key, V value){     ......     //算Hash值     int  hash = hash(key.hashCode());     int  i = indexFor(hash, table.length);     //如果该key已被插入,则替换掉旧的value (链接操作)     for  (Entry<K,V> e = table[i]; e !=  null ; e = e.next) {         Object k;         if  (e.hash == hash && ((k = e.key) == key || key.equals(k))) {             V oldValue = e.value;             e.value = value;             e.recordAccess( this );             return  oldValue;         }     }     modCount++;     //该key不存在,需要增加一个结点     addEntry(hash, key, value, i);     return  null ;}

1 put非null元素后get出来的却是null
源码定位

void  transfer(Entry[] newTable) {     Entry[] src = table;     int  newCapacity = newTable.length;     for  ( int  j =  0 ; j < src.length; j++) {         Entry e = src[j];         if  (e !=  null ) {             src[j] =  null ;//将table[j]设置为null,并发访问到 原table返回的就是null             do  {                 Entry next = e.next;                 int  i = indexFor(e.hash, newCapacity);                 e.next = newTable[i];                 newTable[i] = e;                 e = next;             }  while  (e !=  null );         }     }}

分析: 线程1 将table【j】=null;线程2同时去访问原table,结果违反了直觉

2 多线程put后可能导致get死循环
源码定位

void  resize( int  newCapacity){     Entry[] oldTable = table;     int  oldCapacity = oldTable.length;     ......     //创建一个新的Hash Table     Entry[] newTable =  new  Entry[newCapacity];     //将Old Hash Table上的数据迁移到New Hash Table上     transfer(newTable);//还是这里!!     table = newTable;     threshold = ( int )(newCapacity * loadFactor);}void  transfer(Entry[] newTable){     Entry[] src = table;     int  newCapacity = newTable.length;     //  头插法:从OldTable里摘一个元素出来,然后放到NewTable中     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;//如果此处 线程1 时间片用完,而 线程2 得到调度并将循环执行完,再轮到线程1 调度时问题来了!链表翻转了。                 int  i = indexFor(e.hash, newCapacity);                 e.next = newTable[i];                 newTable[i] = e;                 e = next;             }  while  (e !=  null );         }     }}

分析:
注释出–>如果此处 线程1 时间片用完hang起,而 线程2 得到调度并将循环执行完,再轮到线程1 调度时问题来了!链表翻转了。线程1再次执行transfer重现转移时,由于线程2完成导致链表出现翻转,使得线程1最终构造了一个循环链表,当进行get时,cpu利用100%.参考:
https://my.oschina.net/u/2394328/blog/661626

多线程put的时候可能导致元素丢失
源码定位

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);//这里线程1和线程2同时获取e,执行后必然有一个丢失     if  (size++ >= threshold)         resize( 2  * table.length);}

分析:主要问题出在addEntry方法的new Entry (hash, key, value, e),如果两个线程都同时取得了e,则他们下一个元素都是e,然后赋值给table元素的时候有一个成功有一个丢失。

0 0
原创粉丝点击