docker+jdk+管道流产生线程变相阻塞死锁

2024-01-13 03:50

本文主要是介绍docker+jdk+管道流产生线程变相阻塞死锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

生产出现一个奇葩的问题,某一个应用开发新增了一个异步导出功能,然后提交任务一直是进行中,我让让去检查代码有没有问题,搞了一整天,还是没问题。然后就跟他一起排查这个问题,最先想到的还是业务代码写的有问题,排查了半天终于确定业务代码没问题,然后上生产环境看了下发现生产环境这个项目也出问题了,用的人比较少,所以没人发现。然后开始追朔没人动过这个代码怎么就突然不行了,于是就回退了5个版本终于正常了,然后对比差异,发现,docker容器的基础镜像被人替换了,然后又用jstack 跟踪了下线程
发现线程卡在如下代码
在这里插入图片描述
在这里插入图片描述
线程栈体现出来的是个PipedInputStream管道流,那么回溯代码发现这里确实是个管道流。并且这个管道流启动的两个线程用的是同一个线程池。然后看了下该线程池的线程,而管道流的必须要两个线程才能协同工作一个写一个读,如果,只有一个线程那么就会被阻塞,所以又去看了下线程池配置,发现取得是cpu核心数x2,看了下旧得配置也是一样得,为什么旧得可以,新得包不可以呢?然后看了下jdk得版本,发现一个是1.8.131,不行得那个是1.8.301,去翻了下oracle得官方文档,这里有个区别,是前者在docker中取cpu核心数不准确,取得是宿主机得,而后者取得数docker中得,而我们配置得docker cpu是1核,而宿主机是16核,所以前者可以,后者不可以了。
这个问题真的是坑。
写个简单得测试用例模拟生产得这个问题:代码如下

package com.qimo.omsa.demo.thread;import com.qimo.omsa.demo.jsttool.SSH;
import com.qimo.omsa.demo.jsttool.ServiceDeploy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;/*** @Description TODO* @Author xxx* @Date 2021/9/1 14:37*/
public class ThreadTest {private static ThreadFactory springThreadFactory = new CustomizableThreadFactory("test-async-");private static ExecutorService executor=new ThreadPoolExecutor(1,1,0,TimeUnit.SECONDS,new ArrayBlockingQueue(100),springThreadFactory);public static void main(String[] args) throws Exception {PipedOutputStream pos=new PipedOutputStream();PipedInputStream pis=new PipedInputStream(pos);System.out.println("程序开始");Future<String>  task=executor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {ByteArrayOutputStream os=new ByteArrayOutputStream();byte[] bytes = new byte[8];int len=0;while((len=pis.read(bytes))>-1){System.out.println(len);os.write(bytes,0,len);}os.flush();return "管道流接收数据完成";}});executor.execute(()->{ByteArrayOutputStream baos = new ByteArrayOutputStream();try {baos.write(new String("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").getBytes());baos.writeTo(pos);baos.flush();System.out.println("写数据到管道流");} catch (IOException e) {e.printStackTrace();}});System.out.println("主线程阻塞等待返回");System.out.println(task.get());}}

开始得时候新建一个只有一个线程得线程池然后启动项目发现线程卡再了while((len=pis.read(bytes))>-1)这行代码上
输出日志只有

程序开始
主线程阻塞等待返回

然后把线程数改为2 继续执行
程序输出了日志如下

程序开始
主线程阻塞等待返回
写数据到管道流
8
8
8
8
8
8

原因就是上面得线程先启动了,由于没有数据,所以也不返回-1线程卡住,导致后面得线程被放到线程池得阻塞队列中,一直等待该线程结束,但是该线程又一直阻塞,变相死锁。

但是如果将上面两个线程对调一个位置又可以了,写数据得线程执行完了释放了线程,然后读数据得线程执行了,就可以从缓冲区读到数据,而这样写代码就是一次性写出。

这篇关于docker+jdk+管道流产生线程变相阻塞死锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Java死锁问题解决方案及示例详解

《Java死锁问题解决方案及示例详解》死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下... 目录1、简述死锁的四个必要条件:2、死锁示例代码3、如何检测死锁?3.1 使用 jstack3.2

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Java中常见队列举例详解(非线程安全)

《Java中常见队列举例详解(非线程安全)》队列用于模拟队列这种数据结构,队列通常是指先进先出的容器,:本文主要介绍Java中常见队列(非线程安全)的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一.队列定义 二.常见接口 三.常见实现类3.1 ArrayDeque3.1.1 实现原理3.1.2

SpringBoot3中使用虚拟线程的完整步骤

《SpringBoot3中使用虚拟线程的完整步骤》在SpringBoot3中使用Java21+的虚拟线程(VirtualThreads)可以显著提升I/O密集型应用的并发能力,这篇文章为大家介绍了详细... 目录1. 环境准备2. 配置虚拟线程方式一:全局启用虚拟线程(Tomcat/Jetty)方式二:异步

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.