解释 Java 中的 HashMap 和 ConcurrentHashMap 的区别,以及 HashMap 的线程不安全性 ?

news2025/2/6 15:36:22

Java中的HashMap和ConcurrentHashMap的区别

HashMap 和 ConcurrentHashMap 是Java中两种常用的Map实现,它们在多线程环境下的表现有很大的不同。

HashMap

HashMap 是非线程安全的,这意味着在多线程环境下使用 HashMap 可能会导致数据不一致或其他并发问题。

代码示例:

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

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        // 线程1
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                map.put("key" + i, "value" + i);
            }
        }).start();

        // 线程2
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                map.put("key" + i, "value" + i);
            }
        }).start();

        // 等待两个线程执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("HashMap size: " + map.size()); // 可能小于2000
    }
}

问题:

  • 在上面的代码中,两个线程同时对 HashMap 进行写操作,可能会导致数据覆盖或丢失,最终 HashMap 的大小可能小于2000。
ConcurrentHashMap

ConcurrentHashMap 是线程安全的,它在JDK 1.5中引入,设计目的是为了在高并发环境下提供更好的性能。

代码示例:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        Map<String, String> map = new ConcurrentHashMap<>();

        // 线程1
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                map.put("key" + i, "value" + i);
            }
        }).start();

        // 线程2
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                map.put("key" + i, "value" + i);
            }
        }).start();

        // 等待两个线程执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("ConcurrentHashMap size: " + map.size()); // 应该是2000
    }
}

优点:

  • ConcurrentHashMap 通过分段锁(Segment)机制,允许多个线程同时访问不同的段,从而提高了并发性能。
  • 它提供了比 Hashtable 更好的性能,因为 Hashtable 是对整个表进行加锁。
HashMap的线程不安全性

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

  1. 数据覆盖:多个线程同时写入相同的键时,后写入的值会覆盖先写入的值。
  2. 数据丢失:在扩容过程中,如果多个线程同时进行扩容操作,可能会导致部分数据丢失。
  3. 死循环:在JDK 1.8之前,HashMap 在多线程环境下可能会导致链表形成环,从而引发死循环。

代码示例:

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

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

        // 线程1
        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                map.put(i, i);
            }
        }).start();

        // 线程2
        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                map.put(i, i);
            }
        }).start();

        // 等待两个线程执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("HashMap size: " + map.size()); // 可能小于20000
    }
}

日常开发中的合理化使用建议

  1. 单线程环境:在单线程环境下,优先使用 HashMap,因为它提供了更好的性能。
  2. 多线程环境:在多线程环境下,优先使用 ConcurrentHashMap,以确保线程安全。
  3. 读多写少:如果读操作远多于写操作,可以考虑使用 CopyOnWriteArrayList 或 CopyOnWriteArraySet,它们在写操作时会复制整个数组,适用于读多写少的场景。
  4. 并发控制:如果需要对 HashMap 进行并发控制,可以使用 Collections.synchronizedMap 包装 HashMap,但性能不如 ConcurrentHashMap

代码示例:

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SynchronizedMapExample {
    public static void main(String[] args) {
        Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

        // 线程1
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronizedMap.put("key" + i, "value" + i);
            }
        }).start();

        // 线程2
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronizedMap.put("key" + i, "value" + i);
            }
        }).start();

        // 等待两个线程执行完毕
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("SynchronizedMap size: " + synchronizedMap.size()); // 应该是2000
    }
}

实际开发过程中需要注意的点

  1. 避免热点问题:在使用 ConcurrentHashMap 时,尽量避免所有线程都访问同一个段,可以通过调整初始容量和并发级别来优化。
  2. 迭代器弱一致性ConcurrentHashMap 的迭代器是弱一致性的,不会抛出 ConcurrentModificationException,但可能会反映构造后的修改或不反映构造前的修改。
  3. 内存一致性:在多线程环境下,使用 ConcurrentHashMap 时要注意内存一致性问题,确保读操作能看到最新的写操作结果。
  4. 性能测试:在实际应用中,要对 HashMap 和 ConcurrentHashMap 进行性能测试,选择最适合当前场景的实现。

通过以上分析和建议,可以更好地理解 HashMap 和 ConcurrentHashMap 的区别,并在实际开发中做出合理的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2293854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Vue3 + Vite 项目中使用 Tailwind CSS 4.0

文章目录 首先是我的package.json根据官网步骤VS Code安装插件验证是否引入成功参考资料 首先是我的package.json {"name": "aplumweb","private": true,"version": "0.0.0","type": "module","s…

扣子平台的选择器节点:让智能体开发更简单,扣子免费系列教程(17)

欢迎来到涛涛聊AI。今天&#xff0c;我们来聊聊一个非常实用的工具——扣子平台的选择器节点。即使你不是计算机专业人员&#xff0c;但对计算机操作比较熟悉&#xff0c;这篇文章也能帮你快速上手。我们会从基础知识讲起&#xff0c;一步步带你了解选择器节点的使用方法和应用…

享元模式——C++实现

目录 1. 享元模式简介 2. 代码示例 1. 享元模式简介 享元模式是一种结构型模式。 享元模式用于缓存共享对象&#xff0c;降低内存消耗。共享对象相同的部分&#xff0c;避免创建大量相同的对象&#xff0c;减少内存占用。 享元模式需要将对象分成内部状态和外部状态两个部分…

SSRF 漏洞利用 Redis 实战全解析:原理、攻击与防范

目录 前言 SSRF 漏洞深度剖析 Redis&#xff1a;强大的内存数据库 Redis 产生漏洞的原因 SSRF 漏洞利用 Redis 实战步骤 准备环境 下载安装 Redis 配置漏洞环境 启动 Redis 攻击机远程连接 Redis 利用 Redis 写 Webshell 防范措施 前言 在网络安全领域&#xff0…

react的antd表格自定义图标

将原版的加号换成箭头 自定义图标 安装图标包&#xff1a; npm install --save ant-design/icons 引入&#xff1a; import { RightOutlined, DownOutlined } from ant-design/icons; 参数是一个函数 <Table columns{columns} dataSource{data} indentSize{20}expandIc…

Games104——游戏引擎Gameplay玩法系统:基础AI

这里写目录标题 寻路/导航系统NavigationWalkable AreaWaypoint NetworkGridNavigation Mesh&#xff08;寻路网格&#xff09;Sparse Voxel Octree Path FindingDijkstra Algorithm迪杰斯特拉算法A Star&#xff08;A*算法&#xff09; Path Smoothing Steering系统Crowd Simu…

亚博microros小车-原生ubuntu支持系列:22 物体识别追踪

背景知识 跟上一个颜色追踪类似。也是基于opencv的&#xff0c;不过背后的算法有很多 BOOSTING&#xff1a;算法原理类似于Haar cascades (AdaBoost)&#xff0c;是一种很老的算法。这个算法速度慢并且不是很准。MIL&#xff1a;比BOOSTING准一点。KCF&#xff1a;速度比BOOST…

java进阶之并发编程一ReentrantLock的实际应用和线程中断EXAMPLE

引言:继上一篇ReentrantLock的介绍来做俩个小demo。 实现3个线程分别打印指定数字和线程死锁进行线程中断。 上一篇:<<java进阶之并发编程一ReentrantLock同步锁的学习和syncthronized的区别>> **demo1:**ReentrantLock搭配三个线程分别打印指定的数字,直接上代…

分享2款 .NET 开源且强大的翻译工具

前言 对于程序员而言永远都无法逃避和英文打交道&#xff0c;今天大姚给大家分享2款 .NET 开源、功能强大的翻译工具&#xff0c;希望可以帮助到有需要的同学。 STranslate STranslate是一款由WPF开源的、免费的&#xff08;MIT License&#xff09;、即开即用、即用即走的翻…

SpringBoot+Dubbo+zookeeper 急速入门案例

项目目录结构&#xff1a; 第一步&#xff1a;创建一个SpringBoot项目&#xff0c;这里选择Maven项目或者Spring Initializer都可以&#xff0c;这里创建了一个Maven项目&#xff08;SpringBoot-Dubbo&#xff09;&#xff0c;pom.xml文件如下&#xff1a; <?xml versio…

[LeetCode] 二叉树 I — 深度优先遍历(前中后序遍历) | 广度优先遍历(层序遍历):递归法迭代法

二叉树 基础知识深度优先遍历递归法迭代法&#xff08;栈&#xff09;144# 二叉树的前序遍历94# 二叉树的中序遍历145# 二叉树的后序遍历 广度优先遍历递归法迭代法&#xff08;队列&#xff09;102# 二叉树的层序遍历107# 二叉树的层序遍历 II199# 二叉树的右视图637# 二叉树的…

Python aiortc API

本研究的主要目的是基于Python aiortc api实现抓取本地设备&#xff08;摄像机、麦克风&#xff09;媒体流实现Web端预览。本文章仅仅描述实现思路&#xff0c;索要源码请私信我。 demo-server解耦 原始代码解析 http服务器端 import argparse import asyncio import json…

OpenCV4,快速入门,第二讲:图像色彩空间转换

文章目录 引言一、色彩空间概述1.1 RGB与HSV的区别1.2 HSV的详细含义cvtColor二、cvtColor函数详解2.1 函数原型2.2 参数说明2.3 使用示例三、imwrite函数详解3.1 函数原型3.2 参数说明3.3 使用示例四、完整示例代码五、应用场景与注意事项5.1 HSV的典型应用5.2 注意事项结语引…

86.(2)攻防世界 WEB PHP2

之前做过&#xff0c;回顾一遍&#xff0c;详解见下面这篇博客 29.攻防世界PHP2-CSDN博客 既然是代码审计题目&#xff0c;打开后又不显示代码&#xff0c;肯定在文件里 <?php // 首先检查通过 GET 请求传递的名为 "id" 的参数值是否严格等于字符串 "admi…

RK3588——解决Linux系统触摸屏坐标方向相反问题

问题描述&#xff1a;触摸正常产生中断&#xff0c;但系统上报的触摸坐标不正确&#xff0c;是反向的坐标。 解决办法通过修改设备树添加属性翻转坐标。 注&#xff1a;需确认对应的驱动是否有解析该属性的具体内容&#xff0c;否则仍然无法生效。

面对全球化的泼天流量,出海企业如何观测多地域网络质量?

作者&#xff1a;俞嵩、白玙 泼天富贵背后&#xff0c;技术挑战接踵而至 随着全球化进程&#xff0c;出海、全球化成为很多 Toc 产品的必经之路&#xff0c;保障不同地域、不同网络环境的一致的用户体验成为全球化应用的不得不面对的问题。在跨运营商、跨地域的网络环境中&am…

YOLOv11实时目标检测 | 摄像头视频图片文件检测

在上篇文章中YOLO11环境部署 || 从检测到训练https://blog.csdn.net/2301_79442295/article/details/145414103#comments_36164492&#xff0c;我们详细探讨了YOLO11的部署以及推理训练&#xff0c;但是评论区的观众老爷就说了&#xff1a;“博主博主&#xff0c;你这个只能推理…

自定义序列化数据类型

目录 1. WritableComparable1.1 Writable1.2 Comparable1.3 IntWritable 2. 自定义序列化数据类型RectangleWritable3. 矩形面积计算3.1 Map3.2 Reduce 4. 代码和结果4.1 pom.xml中依赖配置4.2 工具类util4.3 矩形面积计算4.4 结果 参考 本文引用的Apache Hadoop源代码基于Apac…

【Linux网络编程】:URL(encode),HTTP协议,telnet工具

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://mp.csdn…

C语言基础系列【3】VSCode使用

前面我们提到过VSCode有多么的好用&#xff0c;本文主要介绍如何使用VSCode编译运行C语言代码。 安装 首先去官网&#xff08;https://code.visualstudio.com/&#xff09;下载安装包&#xff0c;点击Download for Windows 获取安装包后&#xff0c;一路点击Next就可以。 配…