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

相关文章

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

使用Docker构建Python Flask程序的详细教程

《使用Docker构建PythonFlask程序的详细教程》在当今的软件开发领域,容器化技术正变得越来越流行,而Docker无疑是其中的佼佼者,本文我们就来聊聊如何使用Docker构建一个简单的Py... 目录引言一、准备工作二、创建 Flask 应用程序三、创建 dockerfile四、构建 Docker

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的