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

相关文章

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

禅道Docker安装包发布

禅道Docker安装包发布 大家好, 禅道Docker安装包发布。 一、下载地址 禅道开源版:   /dl/zentao/docker/docker_zentao.zip  备用下载地址:https://download.csdn.net/download/u013490585/16271485 数据库用户名: root,默认密码: 123456。运行时,可以设置 MYSQL_ROOT_P

mac jdk 1.7 dmg 官方版

百度云下载 https://pan.baidu.com/s/1SQiidrPFF5aZr4xlx0ekoQ https://pan.baidu.com/s/1SQiidrPFF5aZr4xlx0ekoQ   补充说明: 实际上oracle对于历史版本的jdk都有归档可以在官方网站上下载,只是需要注册个号就可以了。 地址如下: https://www.oracle.com/cn/java

springboot体会BIO(阻塞式IO)

使用springboot体会阻塞式IO 大致的思路为: 创建一个socket服务端,监听socket通道,并打印出socket通道中的内容。 创建两个socket客户端,向socket服务端写入消息。 1.创建服务端 public class RedisServer {public static void main(String[] args) throws IOException {

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

java线程深度解析(六)——线程池技术

http://blog.csdn.net/Daybreak1209/article/details/51382604 一种最为简单的线程创建和回收的方法: [html]  view plain copy new Thread(new Runnable(){                @Override               public voi

java线程深度解析(五)——并发模型(生产者-消费者)

http://blog.csdn.net/Daybreak1209/article/details/51378055 三、生产者-消费者模式     在经典的多线程模式中,生产者-消费者为多线程间协作提供了良好的解决方案。基本原理是两类线程,即若干个生产者和若干个消费者,生产者负责提交用户请求任务(到内存缓冲区),消费者线程负责处理任务(从内存缓冲区中取任务进行处理),两类线程之