简述 ConcurrentHashMap 和 CopyOnWriteArrayList 的区别以及各自适用场景

简单来说:这两个都是 Java 自带的、多线程一起用也不会乱的数据盒子。区别就是:一个盒子是放【一对一对的数据(名字 + 内容)】,一个盒子是放【一排顺序的数据】。

一、ConcurrentHashMap(存键值对,像字典)

想象它是一本字典:左边是单词(key,不能重复),右边是单词解释(value)。

那么它怎么保证多人同时使用不乱?

旧版本 Hashtable:整本字典上一把大锁。只要有一个人在修改,其他人不管查哪里、改哪里,全都要排队等着,很慢。

ConcurrentHashMap 则是把这本字典里面分成好多好多小格子(哈希桶)。

  • 大家查数据的时候,大多情况不用上锁,可以一起查;
  • 如果有人要修改某一个格子里面的内容:只锁住这一小格,别的格子别人照样可以读、可以改,互不影响。

举例:A 修改 "苹果" 这一格,B 同时修改 "香蕉" 这一格,两个人不用互相等。

特点:

  1. 不能放空:单词和解释都不能是空。 原因:你查一个单词,如果返回空,分不清是【这个单词不存在】,还是【单词对应的解释本身就是空】,多人一起用的时候会搞混。
  2. 你一边翻字典(遍历),别人一边修改:你看到的不一定是最新内容,但是不会直接报错崩溃。
  3. 写东西的速度中等,不算特别慢。修改的时候只影响一小部分数据。
java 复制代码
// 简单例子:字典,水果-数量
ConcurrentHashMap<String, Integer> fruitMap = new ConcurrentHashMap<>();
fruitMap.put("苹果", 10);
fruitMap.put("香蕉", 20);

// 如果不存在才放进去,已经有了就不改
fruitMap.putIfAbsent("苹果", 99);
System.out.println(fruitMap.get("苹果")); // 输出10

二、CopyOnWriteArrayList(有序列表,像队伍排队)

想象它是一条队伍:张三、李四、王五,有先后顺序,可以重复名字,可以按第 1 个、第 2 个找人。

那么它怎么保证多人同时使用不乱?

核心规则:读旧队伍,改副本队伍

  • 所有人查看队伍、找人的时候,直接看现在这个队伍,不用排队、不用上锁,速度很快;
  • 只要有人想改动队伍(加人、踢人、修改某个人):
    1. 先上锁,防止好几个人同时改;
    2. 完整复制一份一模一样的新队伍;
    3. 在新队伍上面做修改;
    4. 改完之后,把大家的视线切换到这个新队伍;
    5. 旧队伍就放着,等没人用了自动清理。

重点:修改的时候,要复制整个队伍!队伍越长,复制越费劲、越慢。

特点:

  1. 可以放 null(队伍里可以有空位置);
  2. 如果你正在看旧队伍,别人已经换了新队伍:你看到的还是旧的人员名单,不会报错;
  3. 读飞快;修改非常慢。队伍一大,每次增删都要复制一大串,很耗性能。
java 复制代码
// 简单例子:人员名单
CopyOnWriteArrayList<String> nameList = new CopyOnWriteArrayList<>();
nameList.add("张三");
nameList.add("李四");
nameList.set(1, "小李"); // 把第二个改成小李
System.out.println(nameList.get(1)); // 小李

简易对比表:

对比 ConcurrentHashMap CopyOnWriteArrayList
里面存什么 一对一对数据(名字:内容) 一排有序的数据,有先后顺序
多人读写思路 盒子分成很多小格子,改哪个格子就锁哪个小格子 读原来的队伍;修改时复制整支队伍,改副本
读速度 快 非常快(不用上锁)
修改速度 还行,只影响一小块 很慢!每次改都要全部复制
能不能放空 都不能空 可以放空
适合场景 存键值信息,读多、写也有一些 名单很少改动,大家反复查看

简易记忆方法:顾名思义。 ConcurrentHashMap:Concurrent 代表并发,HashMap 是键值对集合;也就是多线程可以安全同时读写的键值对容器 。 CopyOnWriteArrayList:CopyOnWrite含义是写的时候复制 ,ArrayList代表有序列表;也就是修改数据时复制数组的有序线程安全列表。

小结

  • ConcurrentHashMap 是多线程安全的字典,把数据分成很多小块,修改只锁一小块,读写速度比较均衡,不能存空值。
  • CopyOnWriteArrayList 是多线程安全的名单。查看名单不用上锁;修改名单要复制一份新名单再改,读很快,修改很慢,适合很少改动的列表。
相关推荐
9527!到!3 小时前
Springboot @value读取配置参数踩坑&推荐解决方案
java·spring boot·spring
十年Java程序媛3 小时前
Java 面试常问 this、super:构造方法调用顺序避坑
java·开发语言·spring boot·面试·个人开发
337803 小时前
5分钟搞懂Spring的IoC和Bean管理
java
学到老活到老3 小时前
类已经加载了,为什么静态代码还没执行?
java·jvm
学到老活到老3 小时前
写了 new,就一定会在堆上分配对象吗?
java·jvm
Web极客码3 小时前
决策模型 + LLM:用 Python 拆解浏览器自动化工作流
java·python·自动化
飞天小牛肉3 小时前
线程池明明没到最大线程数,为什么任务还在排队?
java
用户072367066603 小时前
用 Groovy 做动态 Mock:参数分支、造真数据、模拟异常
java·groovy
yuniko-n3 小时前
【JUC】内存屏障
java·开发语言