SocketInputStream.socketRead0引起线程池提交任务后,futureTask.get超时

本文主要是介绍SocketInputStream.socketRead0引起线程池提交任务后,futureTask.get超时,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1. 现象
    • 2. 结论
    • 3. 相关代码
    • 4. 查看堆栈:
    • 5. 查看submitCall
      • 5.1 ThreadPoolExecutor#execute最终调用了 RunnableFuture#run方法
      • 5.2 从代码层面判断 futureTask.get超时只影响了业务线程(调用futureTask.get的线程),不影响工作线程。
      • 5.3 future.get
        • 5.3.1 测试future.get并不能打断线程池的线程。
    • 6. 查看工作线程为何阻塞
      • 6.1 修复
    • 7. 相关资料

1. 现象

线上发短信、邮箱验证码 的时候超时

在这里插入图片描述

2. 结论

  1. SocketInputStream.socketRead0导致线程阻塞,阻塞后占用了线程池的线程。多次阻塞后最终占用了全部的core线程。新提交的任务只能入队,没有线程来处理。
    由于 socket.read占用了corePoolSize 个 线程池的工作线程worker.thread , 这里一共有10个,全都阻塞了。

    而execute提交一个runnable的时候, 在达到corePoolSize后, 会将其放入workQueue中。直到workQueue满。

    新的任务只能入队(enQueue),不能被消费。

    所以 futureTask.get 一直超时。

  2. futureTask.get(timeout,timeunit)不会导致线程池的工作线程异常。工作线程会继续执行。

3. 相关代码

sendVcWorkerThreadPool是ThreadPoolExecutor的子类WorkerThreadPool
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
WorkerThreadPool
这里workQueue本身是一个优先队列,这里会无限扩容
ps:由于无限扩容, 这里maxinumPoolSize是无效的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

先查找相关调用链的底层日志,发现根本没有调用底层方法。

而其他应用能调用到该底层,一直在输出日志,说明是execute本身有问题。

4. 查看堆栈:

搜索AsyncWorker(ps:自定义的线程池一定要重命名,找问题的时候方便),发现该线程池的10个core线程都处于runnable,且所有线程都在

sendEmail

在这里插入图片描述

5. 查看submitCall

虽然future.get有超时,但是这只能保证业务线程不阻塞。
future.get并不能打断线程池的线程。
在这里插入图片描述
这里的sendVcWorkerThreadPool#submitCall与ThreadPoolExecutor
ThreadPoolExecutor#submit类似
在这里插入图片描述
由于继承ThreadPoolExecutor,所以调用了ThreadPoolExecutor的execute
在这里插入图片描述

5.1 ThreadPoolExecutor#execute最终调用了 RunnableFuture#run方法

  • 调用链
    addWorker()->w.start()->treahd.run()->Worker.runWorker(Worker w)->task.run();

  • task即RunnableFuture ,newTaskFor创建了子类FutureTask

因此 查看FutureTask的run方法

  • FutureTask是对Callable的一层封装。

  • 超时只影响了业务线程(调用futureTask.get的线程),不影响工作线程。

5.2 从代码层面判断 futureTask.get超时只影响了业务线程(调用futureTask.get的线程),不影响工作线程。

FutureTask.run
在这里插入图片描述

运行完毕,设置结果
此时可以使用future.get出结果
在这里插入图片描述

这里让【因为future.get,调用park方法使得等待】的线程 恢复。
在这里插入图片描述

5.3 future.get

在这里插入图片描述

死循环检测是否完成, 超时后,直接return 当前state

Unsafe.park()本地方法休眠当前线程, HotSpot在Linux中中通过调用pthread_mutex_lock函数把线程交给系统内核进行阻塞。
在这里插入图片描述
休眠
在这里插入图片描述

检测的时候,先将当前线程添加到waitNode

在这里插入图片描述

5.3.1 测试future.get并不能打断线程池的线程。

在这里插入图片描述

6. 查看工作线程为何阻塞

虽然已经证明了futureTask.get超时后不会打断线程池的worker.thread,还是需要查看工作线程为何阻塞。
再回顾一下堆栈

在这里插入图片描述
execute的调用链是addWorker()->w.start()->treahd.run()->Worker.runWorker(Worker w)->task.run();
而FutureTask是对Callable的一层封装。

本身是SendEmailCall本身是一个Callable

我们只需要查看SendEmailCall的call方法为何一直在运行。

在这里插入图片描述
transport.connect
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

阻塞到readLine

在这里插入图片描述
到这里就很明显了, 是socket的inputStream调用read的时候阻塞。

在这里插入图片描述

socket是可以设置timeout的。

查找timeout的设置
在这里插入图片描述
getSocket
在这里插入图片描述

to为read超时时间,
cto为连接超时时间
如果不设置则都为永久

在这里插入图片描述

而创建的时候,没有设置timeout
到此就明白了, 设置timeout即可。
在这里插入图片描述

6.1 修复

props.put("mail.smtp.connectiontimeout", "3000");
props.put("mail.smtp.timeout", "3000");

在这里插入图片描述
在这里插入图片描述

debug测试
在这里插入图片描述

修改后

在这里插入图片描述

修改为超短时间 会报错。
在这里插入图片描述

7. 相关资料

线程池中的线程何时死亡?
SocketInputStream.socketRead0引起线程池提交任务后,futureTask.get超时
socket连接代理socketRead0(Native Method) 线程阻塞处理

这篇关于SocketInputStream.socketRead0引起线程池提交任务后,futureTask.get超时的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858495

相关文章

Spring Boot3虚拟线程的使用步骤详解

《SpringBoot3虚拟线程的使用步骤详解》虚拟线程是Java19中引入的一个新特性,旨在通过简化线程管理来提升应用程序的并发性能,:本文主要介绍SpringBoot3虚拟线程的使用步骤,... 目录问题根源分析解决方案验证验证实验实验1:未启用keep-alive实验2:启用keep-alive扩展建

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

如何使用Python实现一个简单的window任务管理器

《如何使用Python实现一个简单的window任务管理器》这篇文章主要为大家详细介绍了如何使用Python实现一个简单的window任务管理器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 任务管理器效果图完整代码import tkinter as tkfrom tkinter i

Spring Boot 集成 Quartz 使用Cron 表达式实现定时任务

《SpringBoot集成Quartz使用Cron表达式实现定时任务》本文介绍了如何在SpringBoot项目中集成Quartz并使用Cron表达式进行任务调度,通过添加Quartz依赖、创... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

Java终止正在运行的线程的三种方法

《Java终止正在运行的线程的三种方法》停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作,停止一个线程可以用Thread.stop()方法,但最好不要用它,本文给大家介绍了... 目录前言1. 停止不了的线程2. 判断线程是否停止状态3. 能停止的线程–异常法4. 在沉睡中停止5

Spring Boot中定时任务Cron表达式的终极指南最佳实践记录

《SpringBoot中定时任务Cron表达式的终极指南最佳实践记录》本文详细介绍了SpringBoot中定时任务的实现方法,特别是Cron表达式的使用技巧和高级用法,从基础语法到复杂场景,从快速启... 目录一、Cron表达式基础1.1 Cron表达式结构1.2 核心语法规则二、Spring Boot中定

Java捕获ThreadPoolExecutor内部线程异常的四种方法

《Java捕获ThreadPoolExecutor内部线程异常的四种方法》这篇文章主要为大家详细介绍了Java捕获ThreadPoolExecutor内部线程异常的四种方法,文中的示例代码讲解详细,感... 目录方案 1方案 2方案 3方案 4结论方案 1使用 execute + try-catch 记录

Git如何修改已提交人的用户名和邮箱

《Git如何修改已提交人的用户名和邮箱》文章介绍了如何修改Git已提交人的用户名和邮箱,包括注意事项和具体步骤,确保操作正确无误... 目录git修改已提交人的用户名和邮箱前言第一步第二步总结git修改已提交人的用户名和邮箱前言需注意以下两点内容:需要在顶层目录下(php就是 .git 文件夹所在的目

Gin框架中的GET和POST表单处理的实现

《Gin框架中的GET和POST表单处理的实现》Gin框架提供了简单而强大的机制来处理GET和POST表单提交的数据,通过c.Query、c.PostForm、c.Bind和c.Request.For... 目录一、GET表单处理二、POST表单处理1. 使用c.PostForm获取表单字段:2. 绑定到结