当前位置: 首页 > news >正文

【JAVA基础面经】线程安全的List

线程安全的List

  • 前言
  • CopyOnWriteArrayList
  • Collections.synchronizedList
  • ReentrantReadWriteLock(了解)

前言

Java 提供了多种实现线程安全 List 的方式,其中最常用的三种是:

方案核心机制适用场景
CopyOnWriteArrayList写时复制,读写分离读多写少(如配置信息、监听器列表)
Collections.synchronizedList装饰器模式,全方法加 synchronized读写均衡,需要快速兼容旧代码
手动 ReentrantReadWriteLock读写锁,读共享,写互斥高并发随机写,需要精细化控制锁粒度

CopyOnWriteArrayList

核心原理:写时复制(Copy-On-Write)

  • 读操作:直接读取底层数组,完全无锁,性能极高。
  • 写操作(add、set、remove):先获取 ReentrantLock,然后复制一份新数组,在新数组上修改,最后通过 volatile 变量将内部引用指向新数组。
publicbooleanadd(Ee){finalReentrantLocklock=this.lock;lock.lock();try{Object[]elements=getArray();intlen=elements.length;Object[]newElements=Arrays.copyOf(elements,len+1);newElements[len]=e;setArray(newElements);// volatile 写returntrue;}finally{lock.unlock();}}

对于初始数组: [A, B, C],array指向旧数组,当写操作执行 add(“D”)时会执行以下步骤:

  1. 加锁
  2. 复制新数组: [A, B, C, D]
  3. 将 array 指向新数组
  4. 解锁
CopyOnWriteArrayList<String>list=newCopyOnWriteArrayList<>();list.add("A");list.add("B");// 多线程读:完全并行,无锁newThread(()->{for(Strings:list){System.out.println(s);}}).start();// 写操作:内部加锁,复制数组list.add("C");

Collections.synchronizedList

Collections.synchronizedList 返回一个内部包装类 SynchronizedList,它在所有方法调用外层包裹了 synchronized (mutex) 同步块。

staticclassSynchronizedList<E>implementsList<E>{finalList<E>list;finalObjectmutex;// 锁对象,默认是 thispublicEget(intindex){synchronized(mutex){returnlist.get(index);}}publicbooleanadd(Ee){synchronized(mutex){returnlist.add(e);}}}

包装器只保证单个方法调用的原子性,但迭代遍历不是单个方法调用,必须在外层手动加锁:

List<String>syncList=Collections.synchronizedList(newArrayList<>());// ❌ 错误示范:这样遍历仍可能抛 ConcurrentModificationExceptionfor(Strings:syncList){System.out.println(s);}// ✅ 正确做法:遍历时手动持有同一把锁synchronized(syncList){for(Strings:syncList){System.out.println(s);}}

Collections.synchronizedList可以将任何 List 实现包装为线程安全的(包括ArrayList、LinkedList),而 Vector 只能自己实现。

ReentrantReadWriteLock(了解)

如果希望多个读操作可以并发(多个线程同时读),写操作互斥(写写互斥,读写互斥)可以使用ReentrantReadWriteLock。

importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.locks.ReadWriteLock;importjava.util.concurrent.locks.ReentrantReadWriteLock;publicclassReadWriteLockList<E>{privatefinalList<E>list=newArrayList<>();privatefinalReadWriteLocklock=newReentrantReadWriteLock();publicvoidadd(Ee){lock.writeLock().lock();try{list.add(e);}finally{lock.writeLock().unlock();}}publicEget(intindex){lock.readLock().lock();try{returnlist.get(index);}finally{lock.readLock().unlock();}}publicintsize(){lock.readLock().lock();try{returnlist.size();}finally{lock.readLock().unlock();}}// 遍历方法:需要在外部加锁,或者返回快照publicvoidforEach(Consumer<E>action){lock.readLock().lock();try{list.forEach(action);}finally{lock.readLock().unlock();}}}
http://www.jsqmd.com/news/667529/

相关文章:

  • [CTF实战]从数字密文到Flag:Base与凯撒的联合破译
  • killall报no process found?先别急,用ps aux | grep查查进程名到底叫啥
  • 用STM32和PID算法,我给自己做了个可调压调流的桌面数控电源(附完整代码)
  • 从空气动力学到代码:Matlab仿真揭秘风机Pm-Wm动态关系
  • 别再死磕教材了!用Protege 5.5.0手把手教你构建第一个知识图谱本体(附避坑指南)
  • UE5——动画混合实战:从原理到高级应用
  • 网络工程师必看:GFP帧结构中的校验(CRC)与加扰到底在防什么?
  • PCB安规设计实战:从理论到Layout的爬电距离与电气间隙精准把控
  • 树莓派4B接口实战:用GPIO控制LED灯,USB连接外设的完整教程
  • Qwen3.5-9B Java八股文深度学习:源码级理解与高频面试题破解
  • Mybatis日志框架实战:从SLF4J门面到Log4j2配置详解
  • Altium Designer 21导入HFSS的DXF文件后,图层混乱、边框不对?看这篇就够了
  • LeetCode 139. 单词拆分:动态规划经典入门题
  • 大气层整合包系统架构解析与深度优化指南
  • DevEco Studio:快速生成一个类的构造函数
  • 告别乱码与格式之争:在Visual Studio C++项目中全面启用UTF-8与.editorconfig
  • 如何用Microsoft PICT在30分钟内生成高质量组合测试用例?提升测试效率的实战指南
  • 当注意力机制遇上全局工作空间理论:MITDeepMind联合推演的AGI意识涌现临界点(精确到10⁻⁴秒级时序建模)
  • 别再只盯着准确率了!用Python的sklearn搞定多分类模型的macro与micro F1-score计算
  • 别再踩坑了!Android 10+ 保存图片到相册的完整流程与权限处理(附完整代码)
  • DevEco Studio:快速生成getter和setter方法
  • 高效解决图表数据提取难题:WebPlotDigitizer完整实战指南
  • 金蝶云单据下推进阶:复杂子单据体与基础数据的精准转换
  • 告别高精地图:用RoadMap和AVP-SLAM的语义地图思路,低成本搞定自动驾驶定位
  • 【花雕动手做】小龙虾 MimiClaw 二次开发:控制四电机麦克纳姆轮实现全向运动
  • 飞书事件订阅避坑指南:从URL验证失败到解密报错,我踩过的那些坑(Java版)
  • Vue2项目实战:从AxiosError到ERR_NETWORK,一站式解决跨域请求难题
  • 【多变量输入单步预测】基于北方苍鹰算法(NGO)优化CNN-BiLSTM-Attention的风电功率预测研究(Matlab代码实现)
  • 告别图层导出噩梦:Photoshop批量导出工具让你工作效率提升300%
  • 开源Text-to-Music:基于Meta模型的本地音乐生成方案