Runtime.getRuntime().exec() 日志阻塞问题定位到解决

    技术2025-10-27  17

    1. 发现问题

    工作中使用java程序导数据,

    该导数据的java程序是通过另外一个java程序通过Runtime.getRuntime().exec() 启动的。

    总是会出现数据传输到一半就卡住了,不会继续传输,也不会失败

    纹丝不动,不生不死

     

    2. 定位问题

    查看java进程pid :jps -lm

    查看java进程占用资源情况: top -p pid

    再查看该java进程中线程资源使用情况: top -H -p pid

    发现该java程序,一点资源也不占用,0cpu 0mem

    也就是该java程序并没有在运行,被挂起了,继续分析

     

    查看Thread Dump

    jstack 2714 查看进程2714的stack

    这里省略了其它不相关的信息

    可以看到两个线程处于TIME_WAITING,一个处于RUNNABLE

    这里需要注意Java的线程状态和操作系统层面的线程状态有点不一样

    操作系统层面一个线程有Ready和Running状态,分别表示就绪和运行状态,但是Java只有RUNNABLE状态

    操作系统虽然从cpu角度认为该线程已经阻塞,不再占用cpu,

    但是Java认为程序虽然不再占用cpu,但是可能还占用着IO,或网络,所以还是认定程序是RUNNABLE,和BLOCKED要区分开

     

    看前面几行信息

    java.lang.Thread.State: RUNNABLE

    at java.io.FileOutputStream.writeBytes(Native Method)

    隔了一段时间,重新打印Thread Dump,发现信息完全一样

    到这里几乎可以判断是 IO 阻塞了,而且根据后面的的信息,可以推断出是LOG日志造成的IO阻塞

    注释掉程序的Console日志模块配置,还是阻塞

     

    上网查了半天,原来是Process p = Runtime.getRuntime().exec()方法挖的坑

    该类提供三个流来处理输入输出错误,注:输入输出都是从父进程角度观察 getInputStream() 标准输入 getOutputStream() 标准输出 getErrorStream() 标准错误 不能同步处理这三个流,否则可能会出现某个流没有被及时处理,一直占用缓冲区, 等缓冲区满了,无法写入数据,导致线程阻塞,进程卡死,对外现象就是进程无法停止,也不占资源,什么反应也没有

     

    3. 解决问题

    不要同步处理,可以另外创建线程来处理缓冲区的流,代码

    Thread t1 = new Thread(new ProcessStreamRunable(pro.getInputStream(), "INPUT")); Thread t2 = new Thread(new ProcessStreamRunable(pro.getErrorStream(), "ERROR")); t1.start(); t2.start();

     

    完工

     

    额外补充一段:

    ==========================补充分割线====================================

    java的线程转储可能很多,不便于观察,可以重定向到文件,慢慢找,

    也可以如下查找

    先使用top -H -p pid 找到操作系统上的线程ID, 第一列就是

    jstack 2714 | grep -A 10 a9f

    -A 10  表示往后多打印10行 a9f 是操作系统线程ID,对应的16进制,就是nid,线程ID通过上面的top命令查看

    这样就可以打印指定线程的转储信息了,这可以定位某个线程的状态,如可以查看占用资源很多的线程到底在干什么

    顺便给出linux打印16进制的命令 printf "%x\n"  2719    十进制 -> 十六进制 printf "%d\n" 0xa9f    十六进制 -> 十进制

     

    Thread Dump字段意思

    prio : 表示线程优先级,就是Thread中定义的这个。Thread类 os_prio : 表示操作系统级别的优先级 tid : 表示Java内的线程ID,同样在Thread类中 nid:表示操作系统级别的线程ID的16进制形式

     

    Processed: 0.009, SQL: 9