面试阿里被质问:ConcurrentHashMap线程安全吗
没啥深入实践的理论系同学 , 在使用并发工具时 , 总是认为把HashMap改为ConcurrentHashMap , 就完美解决并发了呀 。 或者使用写时复制的CopyOnWriteArrayList , 性能更佳呀!技术言论虽然自由 , 但面对魔鬼面试官时 , 我们更在乎的是这些真的正确吗?
1 线程重用导致用户信息错乱生产环境中 , 有时获取到的用户信息是别人的 。 查看代码后 , 发现是使用了ThreadLocal缓存获取到的用户信息 。
ThreadLocal适用于变量在线程间隔离 , 而在方法或类间共享的场景 。若用户信息的获取比较昂贵(比如从DB查询) , 则在ThreadLocal中缓存比较合适 。问题来了 , 为什么有时会出现用户信息错乱?
1.1 案例使用ThreadLocal存放一个Integer值 , 代表需要在线程中保存的用户信息 , 初始null 。先从ThreadLocal获取一次值 , 然后把外部传入的参数设置到ThreadLocal中 , 模拟从当前上下文获取用户信息 , 随后再获取一次值 , 最后输出两次获得的值和线程名称 。
文章插图
固定思维认为 , 在设置用户信息前第一次获取的值始终是null , 但要清楚程序运行在Tomcat , 执行程序的线程是Tomcat的工作线程 , 其基于线程池 。而线程池会重用固定线程 , 一旦线程重用 , 那么很可能首次从ThreadLocal获取的值是之前其他用户的请求遗留的值 。 这时 , ThreadLocal中的用户信息就是其他用户的信息 。
1.2 bug 重现在配置文件设置Tomcat参数-工作线程池最大线程数设为1 , 这样始终是同一线程在处理请求:
server.tomcat.max-threads=1
- 先让用户1请求接口 , 第一、第二次获取到用户ID分别是null和1 , 符合预期
- 用户2请求接口 , bug复现!第一、第二次获取到用户ID分别是1和2 , 显然第一次获取到了用户1的信息 , 因为Tomcat线程池重用了线程 。 两次请求线程都是同一线程:http-nio-45678-exec-1 。
- Tomcat服务器下跑的业务代码 , 本就运行在一个多线程环境(否则接口也不可能支持这么高的并发) , 并不能认为没有显式开启多线程就不会有线程安全问题
- 线程创建较昂贵 , 所以Web服务器会使用线程池处理请求 , 线程会被重用 。 使用类似ThreadLocal工具存放数据时 , 需注意在代码运行完后 , 显式清空设置的数据 。
文章插图ThreadLocal利用独占资源的解决线程安全问题 , 若就是要资源在线程间共享怎么办?就需要用到线程安全的容器 。使用了线程安全的并发工具 , 并不代表解决了所有线程安全问题 。
1.4 ThreadLocalRandom 可将其实例设置到静态变量 , 在多线程下重用吗?current()的时候初始化一个初始化种子到线程 , 每次nextseed再使用之前的种子生成新的种子:
UNSAFE.putLong(t = Thread.currentThread(), SEED,r = UNSAFE.getLong(t, SEED) + GAMMA);如果你通过主线程调用一次current生成一个ThreadLocalRandom实例保存 , 那么其它线程来获取种子的时候必然取不到初始种子 , 必须是每一个线程自己用的时候初始化一个种子到线程 。可以在nextSeed设置一个断点看看:UNSAFE.getLong(Thread.currentThread(),SEED);2 ConcurrentHashMap真的安全吗?我们都知道ConcurrentHashMap是个线程安全的哈希表容器 , 但它仅保证提供的原子性读写操作线程安全 。2.1 案例有个含900个元素的Map , 现在再补充100个元素进去 , 这个补充操作由10个线程并发进行 。开发人员误以为使用ConcurrentHashMap就不会有线程安全问题 , 于是不加思索地写出了下面的代码:在每一个线程的代码逻辑中先通过size方法拿到当前元素数量 , 计算ConcurrentHashMap目前还需要补充多少元素 , 并在日志中输出了这个值 , 然后通过putAll方法把缺少的元素添加进去 。
为方便观察问题 , 我们输出了这个Map一开始和最后的元素个数 。
文章插图- 访问接口
- 初始大小900符合预期 , 还需填充100个元素
- worker13线程查询到当前需要填充的元素为49 , 还不是100的倍数
- 最后HashMap的总项目数是1549 , 也不符合填充满1000的预期
- 王兴称美团优选目前重点是建设核心能力;苏宁旗下云网万店融资60亿元;阿里小米拟增资居然之家|8点1氪 | 美团
- 面临|“熟悉的陌生人”不该被边缘化
- 俄罗斯手机市场|被三星、小米击败,华为手机在俄罗斯排名跌至第三!
- 先别|用了周冬雨的照片,我会成为下一个被告?自媒体创作者先别自乱阵脚
- 美国|印度宣布彻底突破5G难关,美英加澳一片欢呼,一周后白宫怒斥被骗
- 责令|1336款APP被责令整改,三大问题突出
- iPhone|接近8千万!苹果被罚款了!中国iPhone用户这次真的该生气了!
- 误导|苹果又吃巨额罚单,因iPhone防水宣传有误导被重罚9400万
- 覆盖|iPhone13Pro概念机:机身正面被屏幕全覆盖,库克想搞事情?
- 敢动|女生最害怕被“偷看”的3软件,QQ不算啥,第二敢动就“翻脸”
