问题:

在Java程序中,通过Runtime.getRuntime().exec()执行一个Linux脚本导致程序被挂住,而在终端上直接执行这个脚本则没有任何问题。
原因:
先来看Java代码:

public final static void process1(String[] cmdarray) {
Process p = null;
BufferedReader br = null;
try {
p = Runtime.getRuntime().exec(cmdarray);
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (p != null) {
p.destroy();
}
}
}

脚本内容很简单,主要内容是将一个指定的tar.gz文件解压到指定目录中。
程序被挂住后,查看进程列表,发现了几个可疑点:

neil@-bash:~/work/tgz$ps ux | grep dowjones
neil 2079 0.0 0.0 2435492 264 s001 R+ 10:56上午 0:00.00 egrep dowjones
neil 2077 0.0 0.0 2435080 652 ?? S 10:56上午 0:00.24 tar xvf dowjones.tar.gz
neil 2073 0.0 0.0 2435488 792 ?? S 10:56上午 0:00.00 /bin/bash /Users/neil/bin/genova/genova_crm.sh /Users/neil/work/tgz/dowjones.tar.gz /Users/neil/work/dest/dowj

其中genova_crm.sh 就是要执行的脚本,tar xvf dowjones.tar.gz 就是执行解压的命令。
可以看到,程序卡在tar命令上,这个命令被挂住了,非常奇怪的事情。。。
再次查看JDK文档,发现Process的文档上说标准缓冲区大小有限,不正确操作输入输出流时可能导致程序挂住。
单独执行tar xvf dowjones.tar.gz命令时,发现有N多输出,而通过Java执行时,没有看到那些输出。
Java程序中只获取了标准输出流,没有获取错误输出流,那么有可能是错误输出缓冲区满而导致tar命令挂住。

解决方法:
修改Java程序,标准输出流与错误输出流均要处理,保证输出缓冲区不会被堵住。具体作法是用一个异步线程读取标准输出,读完即扔,让主线程读取错误输出流:

public final static void process1(String[] cmdarray) {
try {
final Process p = Runtime.getRuntime().exec(cmdarray);
new Thread(new Runnable() {

@Override
publicvoid run() {
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
try {
while (br.readLine() != null)
;
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
BufferedReader br = null;
br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
br.close();
p.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}

重新执行,发现程序可以正常执行了,tar命令的回显被打印出来了。问题解决。
这可能跟特定的tar包有关,执行tar解压时,明显可以看到回显字符串中有些乱码,回显全部被输出到错误流了。

上述方法可以避免标准输出或错误输出缓冲区满从而挂住主程序的问题,但是需要同时处理两个流,有重复之嫌。如果能把标准输出和错误输出并为一个流,那只需要处理一个流即可。ProcessBuilder提供了这种能力。

创建Process有两种方式,一种就是上述的通Runtime.exec来得到,还有一种可以通ProcessBuilder.start()来产生一个Process实例。
ProcessBuilder可以先设置必要的参数数据,如命令、环境变量、工作目录、重定向错误流到标准输出,然后start()根据这些参数来生成一个Process实例,启动一个子进程来执行相应的命令。
代码如下:

public final static void process(String[] cmdarray) throws Throwable {
ProcessBuilder pb = new ProcessBuilder(cmdarray);
pb.redirectErrorStream(true);
Process p = null;
BufferedReader br = null;
try {
p = pb.start();
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
logger.info("Invoke shell: {}", StringUtils.join(cmdarray, " "));
while ((line = br.readLine()) != null) {
logger.info(line);
}
p.waitFor();
} finally {
if (br != null) {
br.close();
}
if (p != null) {
p.destroy();
}
}
}

public final static void process(String[] cmdarray) throws Throwable {
ProcessBuilder pb = new ProcessBuilder(cmdarray);
pb.redirectErrorStream(true);
Process p = null;
BufferedReader br = null;
try {
p = pb.start();
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
logger.info("Invoke shell: {}", StringUtils.join(cmdarray, " "));
while ((line = br.readLine()) != null) {
logger.info(line);
}
p.waitFor();
} finally {
if (br != null) {
br.close();
}
if (p != null) {
p.destroy();
}
}
}

通过上述代码可以看到,错误流被重定向到标准输出流,那么程序只需要处理标准输出就可以了。

Runtime.getRuntime.exec()执行linux脚本导致程序卡死问题相关推荐

  1. runtime无法执行grep_Runtime.getRuntime.exec()执行linux脚本导致程序卡死有关问题

    Runtime.getRuntime.exec()执行linux脚本导致程序卡死问题 问题: 在Java程序中,通过Runtime.getRuntime().exec()执行一个Linux脚本导致程序 ...

  2. 调用Runtime.getRuntime().exec()执行Linux脚本导致程序挂住的问题分析

    问题: 在Java程序中,通过Runtime.getRuntime().exec()执行一个Linux脚本导致程序被挂住,而在终端上直接执行这个脚本则没有任何问题. 原因: 先来看Java代码: pu ...

  3. Runtime.exec()执行linux shell

    最好的执行系统命令的方法就是写个bat文件或是shell脚本.然后调用,那样修改和实现就简点多了. 现在执行外部命令,主要的方式,还是通过调用所以平台的SHELL去完成,WINDOWS下面就用CMD, ...

  4. Runtime.getRuntime.exec的陷阱

    近期在项目有一个需求,需要通过Java的Runtime.getRuntime().exec()执行外部的一个批处理脚本,发现在执行的时候,出现各种诡异的问题,脚本执行一半出现卡死.而脚本在终端上运行没 ...

  5. java执行python脚本_使用Runtime.getRuntime().exec()在java中调用python脚本

    举例有一个Python脚本叫test.py,现在想要在Java里调用这个脚本.假定这个test.py里面使用了拓展的包,使得pythoninterpreter之类内嵌的编译器无法使用,那么只能采用ja ...

  6. Runtime.getRuntime().exec()操作脚本

    项目场景: 在项目中我们需要调用linux语句给企业微信发邮件 问题描述 在服务器上调用命令却能发送成功,但是使用Runtime.getRuntime().exec()却一直发送不成功 @Overri ...

  7. runtime java_Java runtime.getruntime()从执行命令行程序获得输出

    Java runtime.getruntime()从执行命令行程序获得输出 我正在使用运行时从我的Java程序运行命令提示符命令.但是,我不知道如何获得命令返回的输出. 这是我的代码:Runtime  ...

  8. 解决java使用Runtime.exec执行linux复杂命令不成功问题

    解决java使用Runtime.exec执行linux复杂命令不成功问题 参考文章: (1)解决java使用Runtime.exec执行linux复杂命令不成功问题 (2)https://www.cn ...

  9. 在apk 中执行Runtime.getRuntime().exec adb shell各种命令远程控制其他Android设备(一)

    在手机中可以运行adb命令来远程连接操作其他的Android设备(包括手机.智能电视) 前提,两台设备必须连入同一个局域网络中. 参考方法: public void execShell(String ...

最新文章

  1. 2020应届生:今年秋招也太太太太太难了吧!
  2. PHP命令空间namespace及use的用法实践总结
  3. SNF软件开发机器人-子系统-功能-【列表】自由排序-如何配置?
  4. php 弹窗代码大全,PHP_asp.net弹出窗口代码大全,//关闭,父窗口弹出对话框,子窗 - phpStudy...
  5. Java基础笔记 – 枚举类型的使用介绍和静态导入
  6. python 嵌套型partials(nested partials)的使用
  7. 七乐彩中奖规则表_【开奖】双色球第2020094期开奖结果出炉!你中奖了吗?
  8. 别做码农了,去做一名工程师
  9. Java服务端向客户端写文件_java实现客户端向服务器发送文件
  10. ubuntu-文件管理、编辑
  11. 全球首台商用量子计算机发布!体积如同大象,算力不及笔记本
  12. 当心异步刷新后的脚本文件加载
  13. 物联网老年人健康管理系统源码
  14. [Http权威指南]1.Http概况
  15. ITK-SNAP自动分割应用示例:如何进行乳腺腺体脂肪体积测量
  16. 小学课本的“七桥问题”
  17. web移动端-视口是什么
  18. 拼多多,为老妈打开了“新世界”的大门
  19. 为什么你需要企业架构一文,对企业架构在实施上的启示
  20. 元胞自动机matlab代码 交通流,交通流中的NaSch模型及MATLAB代码元胞自动机

热门文章

  1. 浅谈微分求导+泰勒展开+生成函数
  2. 人工智能未来十大趋势,22年最新
  3. 2022国赛论文及可运行代码
  4. svn合并时候的诡异事件
  5. JDBC操作数据库——resultset的操作小窍门
  6. 使用梯度上升法求解 PCA 问题
  7. CSS:N种使用CSS 绘制三角形的方法
  8. 【Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表
  9. 2022外卖霸王餐程序、外系统霸王餐H5/APP程序源码|美团/饿了么霸王餐系统 粉丝裂变 自带账单 在线支付提现等
  10. 解决cv2.error: OpenCV(4.5.1) /tmp/pip-req-build-n_alixql/opencv/modules/highgui/问题