面试官最爱问!同步集合vs并发集合,区别到底在哪?

软件求生 2025-03-25 09:18:16



故事的开端:一次社招面试的惊心动魄

前不久,我的朋友小王打算跳槽,于是我俩约了个周末,讨论一下社招面试的重点问题。

“小米,我最近面试了好几家公司,发现‘并发容器’几乎是必考问题!”小王一脸苦恼,“你能不能给我讲讲并发容器的实现,还有同步集合和并发集合的区别?”

“这可是个经典问题,咱们从头聊起。”小米喝了一口咖啡,拉开了话匣子。

传统集合的线程安全问题

“在多线程环境下,你能想到最经典的线程安全问题是什么?”小米问道。

小王想了想,说:“比如 ArrayList 在多线程环境下是不安全的,如果多个线程同时修改它,可能会导致 ConcurrentModificationException。”

“没错!”小米点头,“Java 早期的集合类如 ArrayList、HashMap、LinkedList 等都是非线程安全的,多个线程同时读写会发生数据竞争和不可预期的错误。”

线程安全的早期解决方案:同步容器

“Java 为了解决这个问题,在 JDK 1.2 里提供了 Collections.synchronizedXXX 工具方法,把原本线程不安全的集合变成了同步容器。”小米继续解释:

“这种方式的核心思想是给所有方法加 synchronized 锁,保证线程安全。但问题是,锁的粒度太大了,会导致性能下降。”

“这就是同步容器的主要缺陷?”小王问。

“对的。比如在 synchronizedList 里,每次 add()、get() 等方法都会加锁,多个线程访问时,实际上是串行执行,根本没发挥多线程的优势。”

Java 并发包的引入

“到了 JDK 1.5,Java 提供了 java.util.concurrent 包,引入了一系列高效的并发容器,比如 ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue 等。”

“这些并发容器是怎么优化的?”小王迫不及待地问。

“它们的核心思想是减少锁的粒度,甚至无锁化,从而提高并发性能。”小米笑着说,“咱们来看看它们的实现。”

ConcurrentHashMap

“HashMap 在多线程环境下会出现死循环的问题,而 ConcurrentHashMap 通过分段锁(JDK 1.7)或者 CAS + 红黑树(JDK 1.8)来提高并发性能。”

JDK 1.7 版本

JDK 1.7 采用了分段锁(Segment),即把整个 Map 分成多个 Segment,每个 Segment 维护自己的 HashEntry[] 数组:

优点:不同 Segment 可以同时写,避免了 synchronized 加锁整个 HashMap 的性能瓶颈。

缺点:Segment 数量固定,扩展性有限。

JDK 1.8 版本

JDK 1.8 去掉了 Segment,采用了CAS + synchronized + 红黑树:

CAS(compareAndSwap) 实现无锁并发。

synchronized 只锁定冲突的桶,而不是整个 Map。

当链表长度超过 8 时,自动转换为红黑树,提高查询效率。

CopyOnWriteArrayList

“如果我们需要一个线程安全的 ArrayList,用 Collections.synchronizedList() 好,还是 CopyOnWriteArrayList 好?”小米考了小王一个问题。

小王想了想,说:“CopyOnWriteArrayList 适用于读多写少的场景,因为它在写的时候会拷贝一个新的数组。”

“没错!”小米点头,“来看 CopyOnWriteArrayList 的代码:”

写时复制:每次 add()、set() 时,都会创建一个新数组,保证原数组不会被修改。

读写分离:get() 方法可以无锁并发访问,非常适合读多写少的场景,比如缓存、白名单等。

ConcurrentLinkedQueue

“普通的 LinkedList 不是线程安全的,如果要在多线程环境下用队列,该怎么办?”小米继续问。

“可以用 BlockingQueue,但如果不想阻塞的话,ConcurrentLinkedQueue 也是个不错的选择。”小王回答道。

“对的!ConcurrentLinkedQueue 是一个无锁的并发队列,采用 CAS 方式实现。”小米补充道,“它的特点是:

非阻塞,即使队列满了也不会阻塞线程。

适用于高并发环境,特别是生产者-消费者模式。”

总结:同步容器 vs. 并发容器

“最后,我们用一个表格来总结一下同步容器和并发容器的区别吧。”小米说。

尾声:面试的胜利

“哇,这下我明白了!”小王激动地说,“下次面试再遇到这个问题,我肯定能答上来了!”

“哈哈,光懂理论还不够,平时多写代码,多实战。”小米拍了拍小王的肩膀,“面试不只是考察知识点,更是考察你对并发编程的理解。”

第二天,小王顺利通过了面试,收到了心仪公司的 Offer,而小米,也继续在技术的道路上不断前行。

思考与拓展

如果你觉得并发编程很有趣,可以尝试自己实现一个 ConcurrentHashMap 的简化版本,或者研究一下 Disruptor 这类高性能无锁队列,深入理解并发的魅力!

如果你在并发编程方面有任何疑问,欢迎留言交流!

END

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

0 阅读:0

软件求生

简介:从事软件开发,分享“技术”、“运营”、“产品”等。