原文来自于:zha-ge.cn/java/80

面试高频考点!CopyOnWriteArrayList 到底是什么?

先来个场景,某次面试,我原本信心满满,BAT大厂二面官,眉头一扬直接来了句:“那你说说 CopyOnWriteArrayList 吧。”瞬间脑子嗡了一下,这玩意儿不是数组那一挂的么,居然还能成为拷问灵魂的问题!好了,今天就来随意聊聊 CopyOnWriteArrayList。如果你也遇到过类似问题,不妨听听我的“踩坑日记”。


其实这个名字,光看 CopyOnWrite 就挺直白了,写的时候才复制。听起来有点像小时候考试,平时作业抄的安心,真遇到老师收本子,才瑟瑟发抖赶紧抄标准答案……


CopyOnWriteArrayList 忍者般的底气

为啥会用它?两个字:线程安全。 还是有人疑惑,这么多集合,为啥就它特别?

  • 传统的 ArrayList,都知道,线程不安全。
  • Vector?线程安全是安全了点,用的又是笨重的 synchronized,每次加锁解锁,速度慢得像蜗牛。
  • CopyOnWriteArrayList 呢?靠的不是锁,而是“写时复制”——只有写的时候才复制一份新数组。平时读就放心老老实实地用,效率高得很。

顺嘴提一句,CopyOnWriteArrayList 最适合“读多写少”的场景。 比如系统有个配置表,万年不改,偏偏读的人特别多,就它香。


踩坑瞬间

我当初第一次用 CopyOnWriteArrayList,想着线程安全,放心大胆往里怼。哪知道,下面这场面直接把我气笑。

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("蛋炒饭");
list.add("牛奶");

// 每次 add/remove,其实都复制一遍数组
list.remove("蛋炒饭");  // 这一步悄咪咪复制了一份新数组
// ……

问题来了,写操作多起来,内存就猛涨。本来想着省事,没想到一大堆写入,一晚上 jvm 都快被吃挂了。

最迷惑的时候是遍历。 你以为会像普通 ArrayList 那样:

for (String dish : list) {
    // 假装有写操作
    list.remove(dish);
}

结果 CopyOnWriteArrayList 压根不甩你,遍历期间的写操作,压根不报错,也不会“瞬时生效”,遍历用的是遍历前的旧快照,活脱脱“井水不犯河水”,你删除你自己的,遍历我还是老样子,完全看不到你的骚操作。 这跟 ArrayList 那动不动就 ConcurrentModificationException 的暴脾气完全不一样。


经验启示

让我总结一下血泪教训:

  • 别乱用: 只用在读操作远多于写操作的地方。写太多?老老实实乖乖回归锁或者用别的并发集合。
  • 遍历场景:
    • 遍历时看到的是遍历前的内容,中间搞啥写操作都不影响这个遍历。简直“特务间的备份”。
    • 不会抛 ConcurrentModificationException,但也别指望写操作能立马反映出来。
  • 内存消耗: 写操作多,复制开销大,尤其是大列表会哭出来。写少读多才是它的主战场。
  • 面试提问高频点:
    • 与 ArrayList/Vector/其他并发集合的区别
    • 内部实现的原理(复制、快照)
    • 应用场景举例

写到这里

CopyOnWriteArrayList 这个家伙吧,本质上就像写日记:写的时候影印一份,谁爱怎么改怎么改,看的时候一律享受历史快照,不用担心中间被捣乱。优点爽快,缺点明显,得看清场景再下手。

最后,面试如果遇到,别慌,就聊聊它的底层原理+适用场景+注意点,分分钟给面试官来个灵魂三连(其实我后来就这么糊弄过的,效果还挺好)。 大家伙有啥踩坑经历,也欢迎留言吐槽,我们互相取暖~

——溜了,今晚先不写代码,吃点蛋炒饭压压惊。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]