HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么
2023-09-14 09:00:30 时间
Hashmap在并发环境下,可能出现的问题:
1、多线程put时可能会导致get无限循环,具体表现为CPU使用率100%;
原因:在向HashMap put元素时,会检查HashMap的容量是否足够,如果不足,则会新建一个比原来容量大两倍的Hash表,然后把数组从老的Hash表中迁移到新的Hash表中,迁移的过程就是一个rehash()的过程,多个线程同时操作就有可能会形成循环链表,所以在使用get()时,就会出现Infinite Loop的情况
// tranfer()片段
// 这是在resize()中调用的方法,resize()就是HashMap扩容的方法
for (int j = 0; j < src.length; j++) {
Entry e = src[j];
if (e != null) {
src[j] = null;
do {
Entry next = e.next; //假设线程1停留在这里就挂起了,线程2登场
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
线程一:Thread1影响key1
线程二:Thread1影响key2
因为两条线程的影响,倒是出现循环的情况
出现问题的测试代码:
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThread extends Thread {
/**
* 类的静态变量是各个实例共享的,因此并发的执行此线程一直在操作这两个变量
* 选择AtomicInteger避免可能的int++并发问题
*/
private static AtomicInteger ai = new AtomicInteger(0);
//初始化一个table长度为1的哈希表
private static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(1);
//如果使用ConcurrentHashMap,不会出现类似的问题
// private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>(1);
public void run() {
while (ai.get() < 100000) { //不断自增
map.put(ai.get(), ai.get());
ai.incrementAndGet();
}
System.out.println(Thread.currentThread().getName() + "线程即将结束");
}
public static void main(String[] args) {
MyThread t0 = new MyThread();
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
MyThread t5 = new MyThread();
MyThread t6 = new MyThread();
MyThread t7 = new MyThread();
MyThread t8 = new MyThread();
MyThread t9 = new MyThread();
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
t9.start();
}
}
2、多线程put时可能导致元素丢失
原因:当多个线程同时执行addEntry(hash,key ,value,i)时,如果产生哈希碰撞,导致两个线程得到同样的bucketIndex去存储,就可能会发生元素覆盖丢失的情况
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);
}
建议:
使用Hashtable 类,Hashtable 是线程安全的;
使用并发包下的java.util.concurrent.ConcurrentHashMap,ConcurrentHashMap实现了更高级的线程安全;
或者使用synchronizedMap() 同步方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。
相关文章
- 构建高并发高可用的电商平台架构实践[通俗易懂]
- CountDownLatch并发测试
- Juc并发编程07——公平锁真的公平吗(源码剖析)
- 华为20级大佬暴力推荐!并发编程深度解析实战七天杀上GitHub榜首
- 大牛即将带你深入解析java虚拟机:并发设施,内存模型
- 并发与竞争
- c 线程安全的单例模式-c多线程并发处理方式_Java多线程面试题:线程锁+线程池+线程同步等
- 【Java 并发编程】线程锁机制 ( 线程安全 | 锁机制 | 类锁 | 对象锁 | 轻量级锁 | 重量级锁 )
- 控制・Oracle 并发控制:实现安全、稳定业务(oracle并发)
- 揭秘Linux系统的最大并发性能(linux最大并发数)
- Linux线程互斥同步:实现安全并发(linux线程同步互斥)
- mysql行级锁实现数据库并发访问安全(mysql行级锁的使用)
- Linux自旋锁:机制实现系统并发安全(linux自旋锁机制)
- 让Redis安全避免高并发危机(怎么防止redis并发)
- 研究并发读写Redis是否安全(并发读写redis安全吗)
- 多线程并发写入Redis的高效率实现(多线程 写入 redis)
- 深入浅出Oracle中的使用并发(oracle 使用并发)
- 基于Redis™O的分布式锁实现更安全的并发操作(基于redisO分布式锁)
- Redis提升高并发读写速度极致体验(redis高并发读写速度)
- 解决Redis高并发环境下的安全问题(redis高并发安全问题)
- Redis锁实现并发高效的安全释放(redis 锁的释放)