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

Java开发时出现的问题---并发与资源管理深层问题

Java 并发模型基于 JVM 内存模型(JMM),资源管理涉及 IO、线程、锁等关键组件。若对并发语义、资源生命周期理解不透彻,易引发死锁、内存泄漏、数据错乱等严重问题。

1. 并发三大特性(可见性、原子性、有序性)的破坏
  • 可见性问题:多线程共享变量时,若未用volatile或同步机制,线程对变量的修改可能仅保存在工作内存,无法及时刷新到主内存,导致其他线程读取旧值。

    class Flag {boolean stop = false; // 未用volatilevoid setStop() { stop = true; }boolean shouldStop() { return stop; }
    }
    // 线程1循环判断shouldStop(),线程2调用setStop(),线程1可能永远无法退出
    

    原理:JMM 允许编译器和 CPU 对指令重排序,stop变量缺少volatile时,线程 1 可能缓存stop=false,忽略线程 2 的修改。

  • 原子性破坏i++等复合操作(读取 - 修改 - 写入)在多线程下非原子,可能导致结果错误:

    class Counter {int count = 0;void increment() { count++; } // 非原子操作
    }
    // 1000线程并发调用,结果可能小于1000
    

    解决方案:用AtomicInteger(CAS 机制)或synchronized保证原子性。

  • 有序性问题:指令重排序可能打破代码执行顺序,典型案例是单例模式的双重检查锁定(DCL):

    class Singleton {private static Singleton instance; // 未用volatileprivate Singleton() {}static Singleton getInstance() {if (instance == null) { // 第一次检查synchronized (Singleton.class) {if (instance == null) { // 第二次检查instance = new Singleton(); // 可能重排序}}}return instance;}
    }
    

    风险:new Singleton()可分解为 “分配内存→初始化对象→引用指向内存”,重排序后可能变为 “分配内存→引用指向内存→初始化对象”,导致其他线程获取到未初始化的instance。需用volatile禁止重排序。

2. 线程池的参数配置与资源失控
  • 核心参数的误解

    • corePoolSize(核心线程数):线程池保留的最小线程数,若任务数超过此值,会放入工作队列;
    • maximumPoolSize(最大线程数):队列满后允许创建的最大线程数,若超过此值,触发拒绝策略。
      错误配置案例:将corePoolSize设为 0 且队列容量极大,导致每次任务需重新创建线程,抵消线程池复用优势。
  • 拒绝策略的滥用:默认拒绝策略AbortPolicy会抛出RejectedExecutionException,若未处理,可能导致任务丢失。高并发场景下,应根据业务选择CallerRunsPolicy(让提交者线程执行,缓解压力)或自定义策略。

  • 线程泄漏:任务执行时间过长、阻塞(如BlockingQueue.take()无超时)或抛出未捕获异常,会导致线程池线程永久阻塞或终止,逐渐耗尽线程资源。

3. 锁机制的高级陷阱
  • 死锁的隐蔽触发:多线程交叉获取锁且不释放,如线程 1 持有锁 A 等待锁 B,线程 2 持有锁 B 等待锁 A。更隐蔽的场景是 “锁顺序不一致”:不同方法获取锁的顺序不同,在高并发下随机触发死锁。

  • 锁升级与性能损耗:synchronized 锁会从偏向锁→轻量级锁→重量级锁升级,若频繁竞争,会升级为重量级锁(依赖 OS 互斥量),导致性能大幅下降。应避免在热点代码中使用 synchronized,或改用ReentrantLock(可中断、公平锁选择)。

  • 锁粒度不当

    • 过粗:对整个对象加锁(如public synchronized void method()),导致无关操作阻塞;
    • 过细:过度拆分锁(如每个字段加锁),增加锁竞争和上下文切换成本。
4. 资源泄漏的隐蔽形式
  • ThreadLocal 的内存泄漏ThreadLocal通过ThreadThreadLocalMap存储线程私有变量,若ThreadLocal对象被回收,ThreadLocalMap中的Entry(弱引用 key)会变为null,但 value 仍被线程引用,若线程长期存活(如线程池核心线程),value 无法回收,导致内存泄漏。
    解决:使用后调用ThreadLocal.remove()清除 value。

  • NIO 资源未释放SelectorSocketChannel等 NIO 组件若未关闭,会导致文件描述符(FD)泄漏,最终触发Too many open files错误。尤其在网络编程中,需确保close()finally或 try-with-resources 中执行。

  • 数据库连接池的连接泄漏:从连接池获取Connection后未关闭(如conn.close()被遗漏),会导致池内连接耗尽,后续请求阻塞。

http://www.xdnf.cn/news/17090.html

相关文章:

  • 在具身智能火热加持下,看 2025 年机器人学术年会中的热点主题。PNP机器人展示力控、灵巧手捕捉等案例。
  • Android Studio下载及安装配置
  • 计算机视觉的四项基本任务辨析
  • Android audio之 AudioDeviceInventory
  • 飞算JavaAI需求转SpringBoot项目:从零到一的沉浸式开发之旅
  • 人工智能之数学基础:利用全概率公式如何将复杂事件转为简单事件
  • 学习游戏制作记录(将各种属性应用于战斗以及实体的死亡)8.5
  • DM8日常运维命令总结(四)
  • Go语言 string
  • 数据结构——双向链表
  • Linux 调度器函数sched_*系统调用及示例
  • 【音视频】WebRTC 一对一通话-信令服
  • Go语言实战案例:使用context控制协程取消
  • 算法训练之哈希表
  • Java后端高频面试题
  • React在使用create-react-app创建项目慢的解决办法
  • python的高校考研交流系统
  • 基于ARM+FPGA多通道超声信号采集与传输系统设计
  • 广州客户 戴尔R720服务器 liunx系统 RAID5无损升级扩容
  • 注意点:Git 从安装到分支协作、冲突解决的完整步骤 ---待修改,没看这个步骤,需要重新整理步骤
  • JavaWeb(苍穹外卖)--学习笔记17(Websocket)
  • 国产三防平板电脑是什么?三防平板推荐
  • 前端包管理器深度对比
  • VUE2 学习笔记18 路由守卫
  • Mysql使用Canal服务同步数据->ElasticSearch
  • 数据挖掘,到底是在挖掘什么?
  • Golang 基本数据类型
  • 智慧工业复杂目标检测精度跃升:陌讯多模态融合算法实战解析
  • mac前端环境安装
  • 机器学习之KNN、贝叶斯与决策树算法