您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

HashMap线程不安全体现在哪里?

时间:2023-04-27 13:25:57  来源:  作者:晾干的红领巾

 

HashMap线程不安全体现在哪里?如果你到现在还不清楚赶紧看下去,明明白白补一补~。

JAVA中,HashMap是一种常用的数据结构,它以键值对的形式存储和管理数据。然而,由于HashMap在多线程环境下存在线程安全问题,因此在使用时需要格外小心。

简单来说:在 hashMap1.7 中扩容的时候,因为采用的是头插法,所以会可能会有循环链表产生,导致数据有问题,在 1.8 版本已修复,改为了尾插法; 在任意版本的 hashMap 中,如果在插入数据时多个线程命中了同一个槽,可能会有数据覆盖的情况发生,导致线程不安全。

HashMap的线程不安全主要体现在以下两个方面:

1. 并发修改导致数据不一致

HashMap的数据结构是基于数组和链表实现的。在进行插入或删除操作时,如果不同线程同时修改同一个位置的元素,就会导致数据不一致的情况。具体来说,当两个线程同时进行插入操作时,假设它们都要插入到同一个数组位置,并且该位置没有元素,那么它们都会认为该位置可以插入元素,最终就会导致其中一个线程的元素被覆盖掉。此外,在进行删除操作时,如果两个线程同时删除同一个元素,也会导致数据不一致的情况。

以下是一个示例代码,展现了两个线程对HashMap进行并发修改的情况:

import java.util.HashMap;

public class HashMapThreadUnsafeExample {

    public static void mAIn(String[] args) throws InterruptedException {
        final HashMap<String, Integer> map = new HashMap<>();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                map.put("key" + i, i);
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                map.put("key" + i, i * 2);
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("map size: " + map.size());
    }
}
复制代码

上述示例代码中,t1线程和t2线程都向HashMap中插入数据,由于它们在进行插入操作时修改的是同一个位置的元素,因此最终导致了部分数据不一致的情况。例如,当t1线程插入了(key1, 1)以后,t2线程又插入了(key1, 2),这就导致了(key1, 1)被覆盖掉,最终HashMap的大小只有10000而不是20000。

2. 并发扩容导致死循环或数据丢失

当HashMap的元素数量达到一定阈值时,它会触发扩容操作,即重新分配更大的数组并将原来的元素重新映射到新的数组上。然而,在进行扩容操作时,如果不加锁或者加锁不正确,就可能导致死循环或者数据丢失的情况。具体来说,当两个线程同时进行扩容操作时,它们可能会同时将某个元素映射到新的数组上,从而导致该元素被覆盖掉。此外,在进行扩容操作时,如果线程不安全地修改了next指针,就可能会导致死循环的情况。

以下是一个示例代码,展现了两个线程对HashMap进行并发扩容的情况:

import java.util.HashMap;

public class HashMapThreadUnsafeExample {

    public static void main(String[] args) throws InterruptedException {
        final HashMap<String, Integer> map = new HashMap<>(2, 0.75f);

        map.put("key1", 1);
        map.put("key2", 2);
        map.put("key3", 3);

        Thread t1 = new Thread(() -> {
            for (int i = 4; i < 10000; i++) {
                map.put("key" + i, i);
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 4; i < 10000; i++) {
                map.put("key" + i, i * 2);
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("map size: " + map.size());
    }
}
复制代码

上述示例代码中,t1线程和t2线程都向HashMap中插入数据,并且HashMap被初始化为大小为2,负载因子为0.75,这就意味着HashMap在元素数量达到3时就会进行扩容操作。由于t1和t2线程同时进行扩容操作,它们有可能都将某个元素映射到新的数组上,导致该元素被覆盖掉。此外,在进行扩容操作时,如果线程不安全地修改了next指针,就可能会导致死循环的情况。

除了并发修改和并发扩容外,还有以下情况可能导致HashMap不安全:

3. 非线程安全的迭代器

当使用非线程安全的迭代器遍历HashMap时,如果在遍历的过程中其他线程修改了HashMap的结构,就可能抛出
ConcurrentModificationException异常。

以下是一个示例代码,展现了如何通过多线程遍历HashMap以及导致线程不安全的情况:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class HashMapThreadUnsafeExample {

    public static void main(String[] args) throws InterruptedException {
        final Map<String, Integer> map = new HashMap<>();

        for (int i = 0; i < 10000; i++) {
            map.put("key" + i, i);
        }

        Thread t1 = new Thread(() -> {
            Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next().getValue());
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 10000; i < 20000; i++) {
                map.put("key" + i, i);
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }
}
复制代码

上述示例代码中,t1线程遍历了HashMap中的元素,但并没有对其进行加锁保护。同时,在t1线程遍历的过程中,t2线程又进行了另外一部分元素的插入操作,这就导致了HashMap结构的不稳定性,最终可能会抛出
ConcurrentModificationException异常。

4. 非线程安全的比较器

当使用非线程安全的比较器来定义HashMap的排序规则时,就可能导致在并发环境下出现数据不一致性的情况。

以下是一个示例代码,展现了如何通过多线程修改HashMap中元素顺序以及导致线程不安全的情况:

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

public class HashMapThreadUnsafeExample {

    public static void main(String[] args) throws InterruptedException {
        final Map<String, Integer> map = new HashMap<>();

        map.put("key1", 1);
        map.put("key2", 2);
        map.put("key3", 3);

        Comparator<String> comparator = (s1, s2) -> {
            int i1 = Integer.parseInt(s1.substring(3));
            int i2 = Integer.parseInt(s2.substring(3));
            return Integer.compare(i1, i2);
        };

        Thread t1 = new Thread(() -> {
            for (int i = 4; i < 10000; i++) {
                map.put("key" + i, i);
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 4; i < 10000; i++) {
                map.put("key" + i, i * 2);
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("map: " + map);
    }
}
复制代码

上述示例代码中,HashMap的排序规则使用了一个基于字符串处理的比较器来定义。当t1线程和t2线程同时进行插入操作时,由于它们在不同的元素上执行修改操作,因此并不会出现
ConcurrentModificationException异常。然而,由于比较器不是线程安全的,当t1和t2线程同时进行对相同的元素值进行赋值操作时,就可能导致HashMap结构的不稳定性。例如,当t1线程将"key5"的值修改为5时,t2线程可能只修改到"value"字段的一部分,因此最终HashMap中的值可能出现混乱的情况。

写到这里我想告诉大家:HashMap在多线程环境下存在线程安全问题,具体表现为并发修改导致数据不一致和并发扩容导致死循环或数据丢失。因此,在使用HashMap时需要采取相应的线程安全措施,例如使用ConcurrentHashMap、加锁等。



Tags:HashMap   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Search: HashMap  点击:(11)  评论:(0)  加入收藏
HashMap:Java中的高效数据结构
HashMap是Java中常用的数据结构之一,它实现了Map接口,并且提供了快速的查找、插入和删除操作。HashMap的底层数据结构是数组和链表(或红黑树)的组合,这种数据结构被称为哈希表(Has...【详细内容】
2023-11-24  Search: HashMap  点击:(331)  评论:(0)  加入收藏
HashMap的底层数据结构
在 JDK1.8 中,HashMap 还引入了一个新的概念,叫做负载因子(load factor),它是指哈希表中键值对的数量与数组长度的比值。当键值对的数量超过了负载因子与数组长度的乘积时,就会...【详细内容】
2023-09-15  Search: HashMap  点击:(239)  评论:(0)  加入收藏
HashMap 的基础结构,必须掌握!
HashMap 是一种散列表,它存储的内容是键值对(key-value)映射。在 HashMap 中,每个键(key)映射到一个值(value)。散列表的工作原理是:当通过 put() 方法将键值对存储在 HashMap...【详细内容】
2023-09-14  Search: HashMap  点击:(278)  评论:(0)  加入收藏
HashMap 是怎么解决哈希冲突的?
前言 今天来分享一道比较好的面试题,“HashMap 是怎么解决哈希冲突的?”对于这个问题,我们一起看看考察点和比较好的回答吧!考察点 现在的企业级开发中HashMap几乎是...【详细内容】
2023-09-11  Search: HashMap  点击:(198)  评论:(0)  加入收藏
搞懂hashMap底层原理
说明hashMap在java1.7和java1.8版本中有做一些调整,我们本篇只说java1.7的hashMap。数据结构hashMap的数据结构是由数组和链表组成,table是一个存放Entry对象的数组,每个Entry...【详细内容】
2023-08-03  Search: HashMap  点击:(108)  评论:(0)  加入收藏
HashMap线程不安全体现在哪里?
HashMap线程不安全体现在哪里?如果你到现在还不清楚赶紧看下去,明明白白补一补~。在Java中,HashMap是一种常用的数据结构,它以键值对的形式存储和管理数据。然而,由于HashMap在...【详细内容】
2023-04-27  Search: HashMap  点击:(291)  评论:(0)  加入收藏
如何实现线程安全的HashMap?
要实现线程安全的 HashMap,可以考虑以下几种方法: 使用 ConcurrentHashMap:ConcurrentHashMap 是线程安全的 HashMap 实现,采用了分段锁的机制,可以提高并发性能。 使用 Collecti...【详细内容】
2023-03-21  Search: HashMap  点击:(266)  评论:(0)  加入收藏
三分钟轻松搞懂 HashMap 死循环问题!
HashMap 死循环发生在 JDK 1.7 版本中,形成死循环的原因是 HashMap 在 JDK 1.7 使用的是头插法,头插法 + 链表 + 多线程并发 + HashMap 扩容,这几个点加在一起就形成了 HashMap...【详细内容】
2023-01-31  Search: HashMap  点击:(256)  评论:(0)  加入收藏
HashMap核心原理分析
学习目标1、hash冲突的解决办法有哪几种2、HashTable、hashmap、CHM三者之间的区别3、HashMap的默认长度是多少?默认扩容因子是多少?4、HashMap它是怎么解决hash冲突的5、Hash...【详细内容】
2022-09-13  Search: HashMap  点击:(135)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(6)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(13)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(9)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(11)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(9)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条