Java中的共享资源与并发安全问题

发布时间 - 2026-01-06 00:00:00    点击率:
ArrayList线程不安全因其方法无同步控制,多线程并发add可能引发扩容竞态,导致ArrayIndexOutOfBoundsException、数据丢失或ConcurrentModificationException。

为什么多个线程访问同一个 ArrayList 会出错

因为 ArrayList 不是线程安全的:它的 add()remove()get() 等方法内部没有同步控制。当两个线程同时调用 add(),可能触发底层数组扩容,而扩容过程涉及新建数组、复制元素、更新引用——若此时另一个线程正在读或写旧数组,就会出现 ArrayIndexOutOfBoundsException、数据丢失或 ConcurrentModificationException

常见现象包括:集合大小比预期小、遍历时抛 ConcurrentModificationException、偶发 NullPointerException(因元素未完全写入)。

  • 不要在多线程环境中直接共享裸 ArrayListHashMapHashSet
  • 如果只读不写,且初始化完成后不再修改,可用 Collections.unmodifiableList() 包装
  • 若需读多写少,优先考虑 CopyOnWriteArrayList;但注意它每次写操作都复制整个数组,不适合高频写场景

如何正确使用 synchronized 保护临界区

加锁本身不难,关键在于锁对象是否一致、粒度是否合理。错误做法是给每个方法加 synchronized,却用不同锁(如 this vs getClass()),导致实际没互斥。

典型问题:对共享资源的读写必须使用同一把锁;锁范围应刚好覆盖所有依赖该资源的操作,过大会降低并发度,过小则失去保护。

立即学习“Java免费学习笔记(深入)”;

  • 实例方法上加 synchronized,等价于用 this 作锁;静态方法加 synchronized,等价于用 MyClass.class 作锁
  • 若需细粒度控制(例如只锁某个字段),显式使用 synchronized(lockObj),且确保所有访问路径都用同一个 lockObj
  • 避免在同步块内调用外部可重入的方法(如回调、I/O),以防死锁或锁持有时间过长

ReentrantLocksynchronized 多了什么

ReentrantLock 提供更灵活的锁控制能力,但不是“更高级就该默认用它”。它适合需要尝试获取锁、可中断等待、或按特定顺序释放多个锁的场景。

容易忽略的点:ReentrantLock 必须手动 lock()unlock(),且 unlock() 必须放在 finally 块中;否则一旦异常跳出,锁永远无法释放。

  • lock.tryLock() 可避免无限等待,适合超时控制或避免死锁检测
  • lock.lockInterruptibly() 允许线程被 interrupt() 中断,而 synchronized 不响应中断
  • 不要在 ReentrantLocklock()unlock() 之间跨方法调用,除非你明确掌控整个调用链的异常路径
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 访问共享资源
} finally {
    lock.unlock(); // 必须放在这里
}

原子变量(AtomicInteger 等)能替代锁吗

能,但仅限于单个变量的简单操作,比如计数器、状态标志、序列号生成。它们底层依赖 CPU 的 CAS(Compare-And-Swap)指令,无锁且高效。

不能替代锁的场景:多个变量需要保持一致性(如银行转账:从 A 扣款 + 给 B 加款),或操作逻辑复杂(如先查再改再存),这时 CAS 无法保证整体原子性。

  • AtomicIntegerincrementAndGet() 是原子的;但 i++(即 get() + 1set())不是,必须用原子方法
  • 注意 lazySet()set() 的内存语义差异:lazySet() 不保证后续读操作立即看到,适合写后极少读的场景(如统计上报)
  • 不要试图用多个原子变量拼出复合操作的安全性——这通常意味着你需要真正的锁或事务机制
并发安全的核心不在“用了什么工具”,而在“是否识别出所有共享状态,以及是否对所有访问路径施加了一致的同步策略”。很多 bug 出现在边界情况:比如一个线程刚初始化完对象,另一个线程就通过某种间接引用访问到了它——此时连锁对象本身都还没准备好。


# java  # 工具  # 数据丢失  # 无锁  # 为什么 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel怎么实现验证码(Captcha)功能  如何在阿里云高效完成企业建站全流程?  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  使用豆包 AI 辅助进行简单网页 HTML 结构设计  如何用PHP工具快速搭建高效网站?  Linux系统命令中tree命令详解  Laravel distinct去重查询_Laravel Eloquent去重方法  深入理解Android中的xmlns:tools属性  如何在香港服务器上快速搭建免备案网站?  教学论文网站制作软件有哪些,写论文用什么软件 ?  Laravel如何处理异常和错误?(Handler示例)  javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】  如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环  公司网站制作需要多少钱,找人做公司网站需要多少钱?  Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】  如何在云虚拟主机上快速搭建个人网站?  使用PHP下载CSS文件中的所有图片【几行代码即可实现】  如何快速搭建虚拟主机网站?新手必看指南  如何在云主机快速搭建网站站点?  高防服务器租用指南:配置选择与快速部署攻略  车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?  宙斯浏览器视频悬浮窗怎么开启 边看视频边操作其他应用教程  如何续费美橙建站之星域名及服务?  jQuery中的100个技巧汇总  EditPlus中的正则表达式 实战(1)  C++时间戳转换成日期时间的步骤和示例代码  微博html5版本怎么弄发超话_超话进入入口及发帖格式要求【教程】  如何快速搭建高效服务器建站系统?  php静态变量怎么调试_php静态变量作用域调试技巧【解答】  手机网站制作与建设方案,手机网站如何建设?  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  七夕网站制作视频,七夕大促活动怎么报名?  如何快速生成可下载的建站源码工具?  Laravel怎么实现模型属性的自动加密  实例解析angularjs的filter过滤器  在Oracle关闭情况下如何修改spfile的参数  Python自动化办公教程_ExcelWordPDF批量处理案例  如何在新浪SAE免费搭建个人博客?  Laravel怎么实现支付功能_Laravel集成支付宝微信支付  如何选择PHP开源工具快速搭建网站?  html5如何设置样式_HTML5样式设置方法与CSS应用技巧【教程】  胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?  html5audio标签播放结束怎么触发事件_onended回调方法【教程】  详解Huffman编码算法之Java实现  如何快速搭建高效WAP手机网站?  php在windows下怎么调试_phpwindows环境调试操作说明【操作】  Laravel如何与Inertia.js和Vue/React构建现代单页应用  rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted  如何快速重置建站主机并恢复默认配置?