深入解析与解决高并发下的线程池死锁问题

问题背景

在现代互联网应用中,高并发场景是常态,为了高效处理大量用户请求,后端服务通常会采用线程池来管理线程资源。然而,在一个复杂的微服务架构项目中,我们遇到了一个棘手的问题:在业务高峰期,系统频繁出现响应延迟甚至超时的情况,经过初步排查,发现部分服务存在线程池死锁现象,严重影响了系统的稳定性和用户体验。

问题分析

该系统采用Spring Boot框架构建,核心业务模块负责处理用户订单,包括订单创建、支付状态更新以及库存调整等操作。为了提高处理效率,我们为每个处理逻辑配置了独立的线程池。问题主要出现在订单支付成功后的库存减少操作上,具体代码片段如下:

@Service
public class OrderService {

    @Autowired
    private StockService stockService;

    @Async("stockThreadPool")
    public void deductStockAfterPaymentSuccess(Order order) {
        stockService.deduct(order.getProductId(), order.getQuantity());
        // 更新订单状态等后续逻辑...
    }
}

其中,stockThreadPool 是一个自定义配置的线程池,用于处理库存相关的异步操作,以避免库存操作阻塞主线程。然而,在高并发环境下,该线程池经常达到最大线程数,新来的请求因无法获取线程而被无限期地等待,导致线程池死锁。

排查过程
  1. 监控工具辅助:首先,利用JVisualVM、arthas等工具监控系统线程状态,发现stockThreadPool中的线程大多处于WAITING状态,说明存在线程等待资源释放的情况。
  2. 代码审查:检查StockService.deduct()方法实现,发现内部使用了悲观锁(如synchronized或Lock的lock()方法),在高并发下容易形成锁竞争,导致线程等待时间过长。
  3. 日志分析:通过增加详细的日志记录,观察到在某些时间点,多个线程同时尝试锁定相同的商品库存记录,形成了锁链,进而引发了死锁。
解决方案
  1. 优化锁策略:将悲观锁改为乐观锁。在库存服务中,使用版本号或时间戳进行并发控制,减少直接的线程等待。例如,使用@Version注解结合Spring Data JPA的乐观锁机制。
@Entity
public class Product {
    @Id
    private Long id;
    private Integer stock;
    @Version
    private Long version;
    // getters and setters
}
@Service
public class StockService {

    @Transactional
    public boolean deduct(Long productId, Integer quantity) {
        Product product = productRepository.findById(productId)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found"));
        
        if (product.getStock() < quantity) return false;
        
        int updatedStock = product.getStock() - quantity;
        product.setStock(updatedStock);
        
        try {
            product = productRepository.save(product);
        } catch (OptimisticLockException e) {
            // 处理乐观锁失败,通常重试或记录日志
            return false;
        }
        return true;
    }
}

线程池配置优化:根据业务负载情况动态调整线程池参数,如核心线程数、最大线程数、队列大小及拒绝策略等,避免固定配置在高并发下成为瓶颈。使用ThreadPoolExecutor自定义配置,并考虑使用ThreadPoolExecutor.CallerRunsPolicy作为拒绝策略,让调用者线程执行任务,避免直接丢弃任务。

@Configuration
public class ThreadPoolConfig {

    @Bean(name = "optimizedThreadPool")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数,根据CPU核心数和业务需求合理设定
        executor.setCorePoolSize(4 * Runtime.getRuntime().availableProcessors());
        // 最大线程数,防止线程数无限制增长导致资源耗尽
        executor.setMaxPoolSize(executor.getCorePoolSize() * 2);
        // 队列大小,当核心线程都被占用时,新任务将在队列中等待
        executor.setQueueCapacity(500);
        // 拒绝策略,这里采用CallerRunsPolicy,让调用者线程执行任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置线程空闲时间,超过该时间的空闲线程将被终止
        executor.setKeepAliveSeconds(60);
        // 线程名前缀,方便日志追踪
        executor.setThreadNamePrefix("OptimizedThreadPool-");
        // 初始化线程池
        executor.initialize();
        return executor;
    }
}

在服务类中使用自定义线程池:

@Service
public class OrderService {

    @Autowired
    @Qualifier("optimizedThreadPool") // 使用自定义线程池
    private ThreadPoolTaskExecutor executor;

    public void deductStockAfterPaymentSuccess(Order order) {
        executor.execute(() -> {
            stockService.deduct(order.getProductId(), order.getQuantity());
            // 更新订单状态等后续逻辑...
        });
    }
}

增加超时与重试机制:对于可能引起长时间等待的操作,如数据库操作、远程服务调用等,设置合理的超时时间,并在超时后实施重试逻辑,以减少单次请求对系统资源的占用, 使用Future结合自定义的超时和重试。

@Service
public class OrderService {

    // ... 其他代码 ...

    public void deductStockWithRetry(Order order) {
        int retryCount = 0;
        final int maxRetries = 3; // 最大重试次数
        while (retryCount <= maxRetries) {
            try {
                Future<Boolean> resultFuture = executor.submit(() -> stockService.deduct(order.getProductId(), order.getQuantity()));
                // 使用自定义的超时时间,5秒
                if (resultFuture.get(5, TimeUnit.SECONDS)) {
                    System.out.println("库存扣减成功");
                    break; // 成功则跳出循环
                } else {
                    throw new RuntimeException("库存不足,扣减失败");
                }
            } catch (TimeoutException e) {
                System.out.println("库存扣减操作超时,准备重试...");
            } catch (InterruptedException | ExecutionException e) {
                System.out.println("操作异常,准备重试... " + e.getMessage());
            }

            retryCount++;
            if (retryCount > maxRetries) {
                System.out.println("重试次数已达上限,操作失败");
                break;
            } else {
                try {
                    Thread.sleep(1000); // 退避策略,重试前等待一秒
                } catch (InterruptedException ignored) {}
            }
        }
    }
}
成果与思考

经过上述改造,系统在高并发场景下的表现有了显著提升,线程池死锁问题得到有效解决,用户请求响应时间和系统稳定性得到了大幅改善。监控数据显示,线程池利用率更加合理,未再出现请求堆积和长时间等待的情况。

此次经历让我们深刻认识到:

  • 并发控制策略的重要性:合理选择锁策略(乐观锁与悲观锁),能够有效避免死锁和性能瓶颈。
  • 线程池配置的灵活性:动态调整线程池参数,根据实际业务需求和系统负载进行优化,是保障系统稳定的关键。
  • 全面的监控与日志:良好的监控和日志体系是问题定位和系统调优的基石,能够快速定位并解决问题。

总之,面对高并发挑战,开发人员需要综合运用多种技术手段,不断优化和调整,才能确保系统的高效稳定运行。

欢迎扫码关注 微信公众号:JAVA和人工智能
                                                           获取更多免费书籍、资源、优质资料 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/739784.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

收银系统源码-千呼新零售2.0【线上营销】

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看&a…

数据结构-顺序表的插入排序

顺序表的排序可以看作数组排序的拓展。基本逻辑和数组排序的逻辑大同小异。 由于顺序表中可以存放不同种的数据类型&#xff0c;进而和结构体排序又有相似之处。其中要注意的是&#xff08;->&#xff09;和&#xff08;.&#xff09;的区别。 -> 符号是针对指针进行的操…

《计算机英语》Unit 1 Computer Overview 计算机概述

期末试卷组成 1、选择20道 2、判断20道 3、词汇翻译&#xff08;单词词组&#xff0c;参照课后习题&#xff09; 4、翻译2道&#xff08;一道原题&#xff0c;参照作业&#xff09; SectionA About Computer 关于计算机 algorithm n. 算法 operate v.…

6.19长难句打卡

The Flatiron School, where people pay to learn programming, started as one of the many coding bootcamps that’s become popular for adults looking for a career change. 人们在Flatiron学校里花钱学习编程&#xff0c;且Flatiron学校也成为在寻求职业变化的成年人之中…

超越招聘技术人才目标的最佳技术招聘统计数据

研究发现&#xff0c;难以找到的人才比以往任何时候都更难找到&#xff1a;根据新人才委员会招聘调查报告&#xff1a;2024年难以找到的人才的战略和战略&#xff0c;60%的受访者表示&#xff0c;熟练人才的招聘时间比一年前长。调查进一步揭示了以下关于招聘技术的关键事实&am…

与亚马逊云科技深度合作,再获WAPP、ISV认证

上半年&#xff0c;VERYCLOUD睿鸿股份加入亚马逊云科技的WAPP&#xff08;Well-Architected Partner Programs&#xff09;和ISV加速计划&#xff08;ISV Accelerate Program&#xff09;&#xff0c;为客户带来更坚实优质的海外云服务。 Well-Architected 获得WAPP这项认证代表…

揭秘,如何轻松选出那瓶专属于你的心动红酒?

红酒&#xff0c;这个充满神秘与浪漫的液体&#xff0c;总能在不经意间触动我们的味蕾&#xff0c;引发无尽的遐想。然而&#xff0c;面对琳琅满目的红酒选择&#xff0c;如何挑选一瓶适合自己的红酒呢&#xff1f;今天&#xff0c;就让我们一起探讨这个话题&#xff0c;并特别…

艾斯迪克MPU60压力控制单元维修

一、艾斯迪克压力控制单元故障识别与诊断 首先&#xff0c;当出现ESTIC压力控制单元MPU60故障时&#xff0c;我们需要进行故障识别与诊断。这通常包括检查设备的显示屏、指示灯以及传感器等部分&#xff0c;以确定故障的具体位置。此外&#xff0c;还可以使用专业的故障诊断工具…

Conda创建与激活虚拟环境(指定虚拟环境创建位置)

1.Conda优势 Conda是一个开源的软件包管理系统和环境管理系统&#xff0c;主要用于在不同的计算环境中安装和管理软件包和其依赖项。它最初是为Python而设计的&#xff0c;但现在也可以用于管理其他语言的软件包。 Conda提供了对虚拟环境的支持&#xff0c;这使得用户可以在同…

解锁分布式云多集群统一监控的云上最佳实践

作者&#xff1a;在峰 引言 在当今数字化转型加速的时代&#xff0c;随着混合云、多云多集群环境等技术被众多企业广泛应用&#xff0c;分布式云架构已成为众多企业和组织推动业务创新、实现弹性扩展的首选&#xff0c;分布式云容器平台 ACK One&#xff08;Distributed Clou…

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请…

llama系列模型学习

一、目录 llama1 模型与transformer decoder的区别llama2 模型架构llama2 相比llama1 不同之处llama3 相比llama2 不同之处llama、llama2、llama3 分词器词表大小以及优缺点采用的损失函数是什么&#xff1f;为什么Layer Norm 改为RMS Norm?如何消除模型幻觉&#xff1f; 二…

Mac电脑FTP客户端推荐:Transmit 5 for Mac 中文版

Transmit 5是一款专为macOS平台设计的功能强大的FTP&#xff08;文件传输协议&#xff09;客户端软件。Transmit 5凭借其强大的功能、直观易用的界面和高效的性能&#xff0c;成为需要频繁进行文件传输和管理的个人用户和专业用户的理想选择。无论是对于新手还是经验丰富的用户…

KT6368A芯片使用后出现扫描不到蓝牙,2脚持续高电平串口没有反应

KT6368A蓝牙芯片连接问题 问题描述&#xff1a; 蓝牙芯片使用一段时间后&#xff0c;出现扫描不到蓝牙&#xff08;部分芯片出现&#xff0c;出现概率挺高&#xff09;&#xff0c;更换新的芯片后就可以扫描到蓝牙 上电后检测2引脚&#xff08;下图BLE_LINK引脚&#xff09;…

使用vant4+vue3制作电商购物网站

一、前言 1.本项目基于vant4vue3构建&#xff0c;默认友友们已具备相关知识&#xff0c;如不具备&#xff0c;请友友们先去了解相关该概念 2.项目数据来源于开源框架 新峰商城 在此指出 3.此项目目的在于帮助友友们了解基本的用法&#xff0c;没有涉及太多的逻辑操作。 二、…

宝塔面板一键迁移项目站点教程

此插件仅用于将当前机器数据迁移出去&#xff0c;数据接收机器无需安装此插件。 注意事项&#xff1a; 当前教程仅适用《宝塔一键迁移API版本》插件&#xff0c;版本号 >3.0。 推荐迁移面板版本 > 6.9.5&#xff0c;低版本迁移可能存在部分数据无法迁移成功。 面板版…

Future You:对话未来的自己

是由麻省理工开发的 AI 聊天机器人&#xff0c;通过填写一系列表单和上传自己的照片&#xff0c;即可看到老年后的自己并与之对话。

怎么将图片压缩调小?在线压缩图片的4种快捷方法

压缩图片是日常很常用的一个图片处理功能&#xff0c;现在拍摄和制作的图片都比较大&#xff0c;在使用时经常会受到影响。在遇到无法上传、传输过慢的问题时会降低工作效率&#xff0c;所以掌握一招快速压缩图片是非常重要的。通过下面这篇文章来给大家介绍一下在线图片压缩的…

193.回溯算法:组合总和(力扣)

代码解决 class Solution { public:vector<int> res; // 当前组合的临时存储vector<vector<int>> result; // 存储所有符合条件的组合// 回溯函数void backtrcing(vector<int>& nums, int target, int flag, int index) {// 如果当前组合的和超过了…

预备役二招算法测试题解

这次题目出的都是一些偏向于基础的题目&#xff0c;就是一些简单的模拟&#xff0c;思维&#xff0c;以及基础算法&#xff08;二分&#xff0c;前缀和&#xff09; &#xff08;点击题目标题&#xff0c;进入原题&#xff09; 我是签到题 题解&#xff1a;就是说给你 t 组数据…