面试阿里被P8质问:ConcurrentHashMap真的线程安全吗?(上)

简介: 面试阿里被P8质问:ConcurrentHashMap真的线程安全吗?

没啥深入实践的理论系同学,在使用并发工具时,总是认为把HashMap改为ConcurrentHashMap,就完美解决并发了呀。技术言论虽然自由,但面对魔鬼面试官时,我们更在乎的是,这样真的就正确了吗?


我们都知道ConcurrentHashMap是个线程安全的哈希表容器,但它仅保证提供的原子性读写操作线程安全。


让我们看一个案例:

某含若干初始元素的Map,现在打算再填入一些,准备由10个线程并发处理:

image.png

运行时会发现最后ConcurrentHashMap的总元素数不是1000!


这是为什么呢?哪里出错了,说好的线程安全的容器呢?


ConcurrentHashMap这容器本身,可以确保多个线程在put元素时,不会互相干扰,但无法确保线程T1看到还需要put 100个元素但还未put 时,线程T2就看不到容器中的元素数量。

你往这个容器put 100个元素的操作不是原子性的,在其它线程看来可能会有一瞬间,容器里有964个元素,还需要填36个元素。

ConcurrentHashMap对外提供能力的限制:

  • 使用不代表对其的多个操作之间的状态一致,是没有其他线程在操作它的。如果需要确保需要手动加锁
  • 诸如size、isEmpty和containsValue等聚合方法,在并发下可能会反映ConcurrentHashMap的中间状态。因此在并发情况下,这些方法的返回值只能用作参考,而不能用于流程控制。显然,利用size方法计算差异值,是一个流程控制

诸如putAll这样的聚合方法也不能确保原子性,在putAll的过程中去获取数据可能会获取到部分数据

解决方案

整段逻辑加锁:

image.png

既然使用ConcurrentHashMap还要全程加锁,那还不如使用HashMap!不完全是这样。

ConcurrentHashMap提供了一些原子性的简单复合逻辑方法,用好这些方法就可以发挥其威力。这就引申出代码中常见的另一个问题:在使用一些类库提供的高级工具类时,开发人员可能还是按照旧的方式去使用这些新类,因为没有使用其真实特性,所以无法发挥其威力。

案例

使用Map统计Key出现次数:

  • 使用ConcurrentHashMap来统计,Key的范围是10
  • 使用最多10个并发,循环操作1000万次,每次操作累加随机的Key
  • 如果Key不存在的话,首次设置值为1。
  • show me code:
  • image.png
  • 现在我们知道了,应该直接锁住Map,再:
  • 判断
  • 读取现在的累计值
  • +1
  • 保存累加后值

这段代码在功能上的确毫无没有问题,但却无法充分发挥ConcurrentHashMap的性能。

优化后代码:

image.png

ConcurrentHashMap的原子性方法computeIfAbsent做复合逻辑操作,判断K是否存在V,若不存在,则把Lambda运行后结果存入Map作为V,即新创建一个LongAdder对象,最后返回V

因为computeIfAbsent返回的V是LongAdder,是个线程安全的累加器,可直接调用其increment累加。

这样在确保线程安全的情况下达到极致性能,且代码行数骤减。


目录
相关文章
|
29天前
|
安全 Java
Qt经典面试题:Qt开启线程的几种方式
Qt经典面试题:Qt开启线程的几种方式
21 0
|
1月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
102 3
|
3月前
|
存储 缓存 并行计算
【面试问题】JDK并发类库提供的线程池实现有哪些?
【1月更文挑战第27天】【面试问题】JDK并发类库提供的线程池实现有哪些?
|
3月前
|
调度
【面试问题】说说线程的生命周期?
【1月更文挑战第27天】【面试问题】说说线程的生命周期?
|
29天前
|
消息中间件 存储 算法
【C/C++ 泡沫精选面试题04】在实际项目中,多进程和多线程如何选择?
【C/C++ 泡沫精选面试题04】在实际项目中,多进程和多线程如何选择?
43 1
|
1月前
|
Java 调度
金三银四面试必问:线程有几种状态
金三银四面试必问:线程有几种状态
14 0
|
1月前
|
消息中间件 前端开发 NoSQL
面试官:说说线程池的工作原理?
面试官:说说线程池的工作原理?
30 0
|
1月前
|
存储 缓存 Java
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
|
1月前
|
存储 算法
【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解
【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解
|
2月前
|
缓存 监控 架构师
阿里面试:Java开发中,应如何避免OOM
在Java开发中,OutOfMemoryError(OOM)错误一直是令开发者头疼的问题,也是Java面试中出现核心频率很高的问题。 那么我们究竟怎么样才能够有效正确的管理内存,日常开发中究竟要注意哪些核心技巧来避免OOM错误。 本文将带大家一起学习10个避免OOM的实用小技巧,让大家在工作中能够有的放矢,避免OOM错误的飞来横祸。
49 1

热门文章

最新文章