首页 > 其他分享 >【HashMap】HashMap多线程下的死循环问题及JDK8版本的修复

【HashMap】HashMap多线程下的死循环问题及JDK8版本的修复

时间:2023-02-23 14:34:11浏览次数:44  
标签:hash HashMap int JDK8 key Entry table 多线程

背景

想要记录一下关于jdk下的 hashmap 存在的一些问题:
1、许多同学都知道JDK下的 HashMap 是线程不安全的,但究竟是怎样个不安全法,在多线程下会出现怎样的问题?其中原因是什么?

多线程下HashMap可能会出现的问题

1、多线程put操作后,可能会导致元素丢失
2、往里面put元素的时候,可能会产生闭环的链表,get的时候会产生死循环(jdk8已经修复)

问题1,也不仅是 hashmap,在多线程下操作同个对象,也是很常见的了:
线程1 和 线程2 同时put值,并且发生hash 碰撞,同个hash槽本是链表,因为多个线程同时put给链表的第一个位置,后元素把前元素覆盖掉,导致元素丢失(值被改变)。

问题2:死循环问题

出现死循环是要满足几个条件的,多个线程同时往 map 里面 put 元素,并且因为 容量触发阈值,进行 resize() 进行扩容,在扩容过程中把 table[] 扩大两倍,
resize() 方法里面会把 旧table 里面的值迁移到新 table 里面去(并且,会把元素里面的值反序,也正是因为会把链表反序,才会出现死循环的问题,如果不反序,也就会少了个问题,JDK8在元素较多时,将链表转成树,也是解决了这个问题吧)

再贴一下代码

HashMap的rehash源代码(JDK8之前的)

// Put一个Key,Value对到Hash表中:

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;
}


// 检查容量是否超标
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);
	//查看当前的size是否超过了我们设定的阈值threshold,如果超过,需要resize
	if (size++ >= threshold)
		resize(2 * table.length);
} 


// 新建一个更大尺寸的hash表,然后把数据从老的Hash表中迁移到新的Hash表中。
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);
}

// 把table的元素填充到 newTable
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;
				int i = indexFor(e.hash, newCapacity);
				e.next = newTable[i];
				newTable[i] = e;
				e = next;
			} while (e != null);
		}
	}
} 
从这个方法,可以看到,其实它会把链表反一下(如果有jdk7及之前的,可以跟一下就更容易看了)

关于这个问题,其实是发生在 JDK8版本之前才会的,JDK8及之后就修复了这个问题了,所以。。。知道就行吧。
但多线程下,即使不会出现死循环问题,但还是建议使用 ConcurrentHashMap 吧。

关于死循环问题,可参考: https://coolshell.cn/articles/9606.html
说得更清楚些,里面附了一些图解,有助理解。

(完毕~)

标签:hash,HashMap,int,JDK8,key,Entry,table,多线程
From: https://www.cnblogs.com/aaacarrot/p/17147858.html

相关文章

  • TFlite 多线程并行
    importnumpyasnpfrommultiprocessingimportProcess,Queuedefprocess_data(data,model_TSNet,tsnet_input,tsnet_output,queue):model=tflite_runti......
  • 多线程
    介绍获得线程号#include<pthread.h>pthread_tpthread_self(void);功能:得到线程id参数:无返回值:调用此函数的线程id创建线程#include<pthread.h>intpthrea......
  • java socket网络编程(多线程技术)
    Client.javaimportjava.io.*;importjava.net.*;importjava.util.*;publicclassClient{publicstaticvoidmain(Stringargs[]){Scannerscann......
  • 创建多线程方式3:实现Callable接口 改造下载图片案例
    packagecom.Test;importorg.apache.commons.io.FileUtils;importjava.io.File;importjava.io.IOException;importjava.net.URL;importjava.util.concurrent.*;//开......
  • Java多线程技能-方法介绍
    java多线程技能技术点:线程的启动如何使线程暂停如何使线程停止线程的优先级线程安全相关的问题方法currentThread()currentThread()方法可返回代码段正在被哪......
  • 多线程 龟兔赛跑案例
    packagecom.Java;publicclassRaceimplementsRunnable{//胜利者privatestaticStringwinner;@Overridepublicvoidrun(){for(inti=0......
  • java多线程:详解JUC
    对应狂神说JUC视频1.JUC是什么java.util下的几个包的简称涉及到多线程的开发java.util.concurrentjava.util.atomicjava.util.concurrent.locks2.线程和进程进程:多个程序......
  • Java的多线程+Socket
    客户端: packagecom.wulala;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net......
  • debian11安装配置jdk8
    来源参考 https://blog.csdn.net/m0_57194110/article/details/128847508 1.下载jdk8JavaDownloads|Oracle下载 jdk-8u361-linux-x64.tar.gz 文件就可以了  ......
  • Python 多线程中的 Join Lock 和 Event
    Join函数的作用Join函数的作用主要是提供当前线程阻塞,等待线程结束后,在执行下一个线程,保护线程通畅有序执行如下当没有使用join时,主线程结束了子线程还在运行defd......