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+管道流产生线程变相阻塞死锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/a807719447/article/details/122739351
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/600160

相关文章

java -jar example.jar 产生的日志输出到指定文件的方法

《java-jarexample.jar产生的日志输出到指定文件的方法》这篇文章给大家介绍java-jarexample.jar产生的日志输出到指定文件的方法,本文给大家介绍的非常详细,对大家的... 目录怎么让 Java -jar example.jar 产生的日志输出到指定文件一、方法1:使用重定向1、

SpringBoot实现虚拟线程的方案

《SpringBoot实现虚拟线程的方案》Java19引入虚拟线程,本文就来介绍一下SpringBoot实现虚拟线程的方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录什么是虚拟线程虚拟线程和普通线程的区别SpringBoot使用虚拟线程配置@Async性能对比H

在Java中实现线程之间的数据共享的几种方式总结

《在Java中实现线程之间的数据共享的几种方式总结》在Java中实现线程间数据共享是并发编程的核心需求,但需要谨慎处理同步问题以避免竞态条件,本文通过代码示例给大家介绍了几种主要实现方式及其最佳实践,... 目录1. 共享变量与同步机制2. 轻量级通信机制3. 线程安全容器4. 线程局部变量(ThreadL

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

在macOS上安装jenv管理JDK版本的详细步骤

《在macOS上安装jenv管理JDK版本的详细步骤》jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,:本文主要介绍在macOS上安装... 目录前言安装 jenv添加 JDK 版本到 jenv切换 JDK 版本总结前言China编程在开发 Java

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完