`
berdy
  • 浏览: 515537 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

调用Runtime.exec()的一些陷阱

阅读更多
    Runtime 封装着java程序的运行时环境。通过Runtime实例,java应用能够与其运行的环境连接。Runtime在jvm中保持一个单例,所以不能通过Runtime类的构造函数。只能通过Runtime.getRuntime()来获的当前Runtime的一个实例。获得Runtime实例后,就可以通过Runtime的exec()方法在当前jvm进程外启动其他进程了。很常见的一个应用就是,启动浏览器进程来显示一个程序的帮助页面。

在Runtime类中存在四个exec()重载方法.
public Process exec(String command);
public Process exec(String [] cmdArray);
public Process exec(String command, String [] envp);
public Process exec(String [] cmdArray, String [] envp);

主要参数是要启动进程的名称,以及启动该进程时需要的参数。然后是一些环境相关的属性。envp是已name=value,
形式传入的。具体查看下源码便一目了然了。

    通常,启动另外一个进程后,需要获取另外一个进程的执行结果,然后根据结果执行后续的流程。要获取外部进程的运行结果,可以调用Process的exitValue() 方法。下面代码中启动一个java编译器进程。
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("javac");
	int exitVal = proc.exitValue();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

不幸的是,你将看到下面的结果:
java.lang.IllegalThreadStateException: process has not exited
at java.lang.ProcessImpl.exitValue(Native Method)
at com.test.runtime.Test.BadExecJavac(Test.java:13)
at com.test.runtime.Test.main(Test.java:5)

原因是exitValue()方法并不会等待外部进程结束。如果外部进程还未结束,exitValue()将会抛出IllegalThreadStateException。解决办法就是调用Process的waitfor()方法。waitfor()方法会挂起当前线程,一直等到外部进程结束。当然使用exitValue()或者waitfor()完全取决你的需求。可以设个boolean标志,来确定使用哪个。运行下面的代码:
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("javac");
	int exitVal = proc.waitFor();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

发现程序被阻塞了,什么原因呢?JDK's Javadoc文档解释说:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
翻译:
一些平台只为标准输入输出提供有限的缓存。错误的写子进程的输入流或者错误的都子进程的输出流都有可能造成子进程的阻塞,甚至是死锁。

解决上面问题的办法就是程序中将子进程的输出流和错误流都输出到标准输出中。
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("javac");
	InputStream stderr = proc.getErrorStream();
	InputStreamReader isr = new InputStreamReader(stderr);
	BufferedReader br = new BufferedReader(isr);
	String line = null;
	System.out.println("<ERROR>");
	while ((line = br.readLine()) != null)
		System.out.println(line);
	System.out.println("</ERROR>");
	int exitVal = proc.waitFor();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

上面的代码中仅仅是输出了错误流,并没有输出子进程的输出流。在程序中最好是能将子进程的错误流和输出流都能输出并清空。

在windows系统中,很多人会利用Runtime.exec()来调用不可执行的命令。例如dir和copy;
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("dir");
	InputStream stdin = proc.getInputStream();
	InputStreamReader isr = new InputStreamReader(stdin);
	BufferedReader br = new BufferedReader(isr);
	String line = null;
	System.out.println("<OUTPUT>");
	while ((line = br.readLine()) != null)
		System.out.println(line);
	System.out.println("</OUTPUT>");
	int exitVal = proc.waitFor();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

运行上面的代码,将会得到一个错误代码为2的错误。在win32系统中,error=2表示文件未找到。也就是不存在dir.exe和copy.exe。这是因为dir命令式windows中命令行解析器的一部分,并不是单独的一个可执行的命令。要运行上面的命令,得先启动windows下的命令行解析器command.com或者cmd.exe,这个取决于windows的系统的版本。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

class StreamGobbler extends Thread {
	InputStream is;
	String type;

	StreamGobbler(InputStream is, String type) {
		this.is = is;
		this.type = type;
	}

	public void run() {
		try {
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String line = null;
			while ((line = br.readLine()) != null)
				System.out.println(type + ">" + line);
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
}

public class GoodWindowsExec {
	public static void main(String args[]) {
		if (args.length < 1) {
			System.out.println("USAGE: java GoodWindowsExec <cmd>");
			System.exit(1);
		}

		try {
			String osName = System.getProperty("os.name");
			String[] cmd = new String[3];
			if (osName.equals("Windows NT")) {
				cmd[0] = "cmd.exe";
				cmd[1] = "/C";
				cmd[2] = args[0];
			} else if (osName.equals("Windows 95")) {
				cmd[0] = "command.com";
				cmd[1] = "/C";
				cmd[2] = args[0];
			}

			Runtime rt = Runtime.getRuntime();
			System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);
			Process proc = rt.exec(cmd);

			StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

			StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

			errorGobbler.start();
			outputGobbler.start();

			int exitVal = proc.waitFor();
			System.out.println("ExitValue: " + exitVal);
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
}

另外,Runtime.exec()并不是命令解析器,这是启动某个进程。并不能执行一些命令行的命令。下面是一个常见的错误:
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("java jecho 'Hello World' > test.txt");

	StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

	StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

	errorGobbler.start();
	outputGobbler.start();

	int exitVal = proc.waitFor();
	System.out.println("ExitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

上面的代码希望像DOS系统中一样将命令的执行结果输出到文件中去。但是Runtime.exec()并不是命令行解析器。要想重定向输出流,必须在程序中编码实现。
import java.util.*;
import java.io.*;

class StreamGobbler extends Thread {
	InputStream is;
	String type;
	OutputStream os;

	StreamGobbler(InputStream is, String type) {
		this(is, type, null);
	}

	StreamGobbler(InputStream is, String type, OutputStream redirect) {
		this.is = is;
		this.type = type;
		this.os = redirect;
	}

	public void run() {
		try {
			PrintWriter pw = null;
			if (os != null)
				pw = new PrintWriter(os);
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String line = null;
			while ((line = br.readLine()) != null) {
				if (pw != null)
					pw.println(line);
				System.out.println(type + ">" + line);
			}
			if (pw != null)
				pw.flush();
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
}

public class GoodWinRedirect {
	public static void main(String args[]) {
		if (args.length < 1) {
			System.out.println("USAGE java GoodWinRedirect <outputfile>");
			System.exit(1);
		}

		try {
			FileOutputStream fos = new FileOutputStream(args[0]);
			Runtime rt = Runtime.getRuntime();
			Process proc = rt.exec("java jecho 'Hello World'");

			StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
			StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT", fos);

			errorGobbler.start();
			outputGobbler.start();

			int exitVal = proc.waitFor();
			System.out.println("ExitValue: " + exitVal);
			fos.flush();
			fos.close();
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
}

参考资料:
When Runtime.exec() won't
3
0
分享到:
评论
3 楼 di1984HIT 2014-07-30  
用cmd /c java -jar a.jar 1>a.log 2>&1应该可以。
2 楼 wancaibida 2012-05-25  
先处理getErrorStream就不会卡在那儿了
1 楼 wfeng007 2011-10-26  
不错不错
很多java写的本地脚本接口都需要这个....

相关推荐

    when Runtime.exec&#40;&#41; won't 中文翻译版

    ### Java中的Runtime.exec()陷阱详解 #### 引言 在Java编程中,`java.lang.Runtime` 类提供了与Java虚拟机的运行时环境交互的方法,其中`Runtime.exec()` 方法尤其受到开发者的青睐,因为它允许Java程序启动和控制...

    利用Python+Java调用Shell脚本时的死锁陷阱详解

    2. **标准输入、输出和错误**:调用`subprocess.Popen()`或`Runtime.getRuntime().exec()`时,正确设置`stdin`, `stdout`和`stderr`,特别是当它们需要被读取或写入时。 3. **同步与异步**:如果可能,异步处理子...

    Java问题定位技术.pdf

    在介绍Java泥潭问题时,文档提到了Runtime.getRuntime().exec()的不稳定性和JDK自带的Timer类的使用场合,以及池的合理设计,例如对象池、线程池和连接池的设计。还特别提醒了JDK1.5线程池和Timer使用的陷阱。 最后...

    java问题定位技术+性能优化

    - 使用`Runtime.getRuntime().exec()`启动子进程时可能会遇到的问题。 - 需要确保正确处理子进程的输入输出流,防止阻塞。 - **6.2 JDK自带的几个Timer的适用场合** - `java.util.Timer`: 适用于简单的计时任务。 ...

    [附源码+数据库+毕业论文+部署教程+配套软件]基于SpringBoot+MyBatis+MySQL+Maven+Vue的停车场管理系统,推荐!

    一、项目简介 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷 二、技术实现 jdk版本:1.8 及以上 ide工具:IDEA或者eclipse 数据库: mysql5.5及以上 后端:spring+springboot+mybatis+maven+mysql 前端: vue , css,js , elementui 三、系统功能 1、系统角色主要包括:管理员、用户 2、系统功能 前台功能包括: 用户登录 车位展示 系统推荐车位 立即预约 公告展示 个人中心 车位预定 违规 余额充值 后台功能: 首页,个人中心,修改密码,个人信息 用户管理 管理员管理 车辆管理 车位管理 车位预定管理,统计报表 公告管理 违规管理 公告类型管理 车位类型管理 车辆类型管理 违规类型管理 轮播图管理 详见 https://flypeppa.blog.csdn.net/article/details/146122666

    springboot656基于java-springboot的农机电招平台毕业设计(代码+数据库+论文+PPT+演示录像+运行教学+软件下载).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql 部署环境:maven 数据库工具:navica 更多毕业设计https://cv2022.blog.csdn.net/article/details/124463185

    Python程序设计学习思维导图-仅供参考

    内容为Python程序设计的思维导图,适用于新手小白进行浏览,理清思路

    2024-Stable Diffusion全套资料(软件+关键词+模型).rar

    2024-Stable Diffusion全套资料(软件+关键词+模型).rar

    mmexport1741417035005.png

    mmexport1741417035005.png

    COMSOL三维锂离子电池全耦合电化学热应力模型:模拟充放电过程中的多物理场耦合效应及电芯内应力应变情况,COMSOL锂离子电池热应力全耦合模型,comsol三维锂离子电池电化学热应力全耦合模型锂离子

    COMSOL三维锂离子电池全耦合电化学热应力模型:模拟充放电过程中的多物理场耦合效应及电芯内应力应变情况,COMSOL锂离子电池热应力全耦合模型,comsol三维锂离子电池电化学热应力全耦合模型锂离子电池耦合COMSOL固体力学模块和固体传热模块,模型仿真模拟电池在充放电过程中由于锂插层,热膨胀以及外部约束所导致的电极的应力应变情况结果有电芯中集流体,电极,隔膜的应力应变以及压力情况等,电化学-力单向耦合和双向耦合 ,关键词: 1. COMSOL三维锂离子电池模型; 2. 电化学热应力全耦合模型; 3. 锂离子电池; 4. 固体力学模块; 5. 固体传热模块; 6. 应力应变情况; 7. 电芯中集流体; 8. 电极; 9. 隔膜; 10. 电化学-力单向/双向耦合。,COMSOL锂离子电池全耦合热应力仿真模型

    基于传递矩阵法的一维层状声子晶体振动传输特性及其优化设计与应用,声子晶体传递矩阵法解析及应用,Matlab 一维层状声子晶体振动传输特性 传递矩阵法在声子晶体的设计和应用中具有重要作用 通过调整声子

    基于传递矩阵法的一维层状声子晶体振动传输特性及其优化设计与应用,声子晶体传递矩阵法解析及应用,Matlab 一维层状声子晶体振动传输特性 传递矩阵法在声子晶体的设计和应用中具有重要作用。 通过调整声子晶体的材料、周期和晶格常数等参数,可以设计出具有特定带隙结构的声子晶体,用于滤波、减震、降噪等应用。 例如,通过调整声子晶体的周期数和晶格常数,可以改变带隙的位置和宽度,从而实现特定的频率范围内的噪声控制。 此外,传递矩阵法还可以用于分析和优化声子晶体的透射谱,为声学器件的设计提供理论依据。 ,Matlab; 一维层状声子晶体; 振动传输特性; 传递矩阵法; 材料调整; 周期和晶格常数; 带隙结构; 滤波; 减震; 降噪; 透射谱分析; 声学器件设计,Matlab模拟声子晶体振动传输特性及优化设计研究

    头部姿态估计(HeadPose Estimation)-Android源码

    头部姿态估计(HeadPose Estimation)-Android源码

    永磁同步电机FOC、MPC与高频注入Simulink模型及基于MBD的代码生成工具,适用于Ti f28335与dspace/ccs平台开发,含电机控制开发文档,永磁同步电机控制技术:FOC、MPC与高

    永磁同步电机FOC、MPC与高频注入Simulink模型及基于MBD的代码生成工具,适用于Ti f28335与dspace/ccs平台开发,含电机控制开发文档,永磁同步电机控制技术:FOC、MPC与高频注入Simulink模型开发及应用指南,提供永磁同步电机FOC,MPC,高频注入simulink模型。 提供基于模型开发(MBD)代码生成模型,可结合Ti f28335进行电机模型快速开发,可适用dspace平台或者ccs平台。 提供电机控制开发编码器,转子位置定向,pid调试相关文档。 ,永磁同步电机; FOC控制; MPC控制; 高频注入; Simulink模型; 模型开发(MBD); Ti f28335; 电机模型开发; dspace平台; ccs平台; 编码器; 转子位置定向; pid调试。,永磁同步电机MPC-FOC控制与代码生成模型

    light of warehouse.zip

    light of warehouse.zip

    考虑温度和气体排放等因素的工业乙醇发酵过程及其Matlab源码-乙醇发酵-气体排放-Matlab建模和仿真-代谢路径

    内容概要:文章深入讨论了工业乙醇发酵的基本原理及工艺流程,特别是在温度和气体排放(如CO2及其他有害气体)影响下的发酵效果分析。文章介绍了乙醇发酵的重要环节,如糖分解、代谢路径、代谢调控以及各阶段的操作流程,重点展示了如何通过Matlab建模和仿真实验来探索这两个关键环境因素对发酵过程的具体影响。通过动态模型仿真分析,得出合适的温度范围以及适时排除CO2能显著提升发酵产乙醇的效果与效率,从而提出了基于仿真的优化发酵生产工艺的新方法。 适用人群:从事生物工程相关领域研究的科学家、工程师及相关专业师生。 使用场景及目标:适用于实验室环境、学术交流会议及实际生产指导中,以提升研究人员对该领域内复杂现象的理解能力和技术水平为目标。 其他说明:附录中有详细的数学公式表达和程序代码可供下载执行,便于有兴趣的研究团队重复实验或者继续扩展研究工作。

    Tomcat资源包《Tomcat启动报错:CATALINA-HOME环境变量未正确配置的完整解决方案》

    本资源包专为解决 Tomcat 启动时提示「CATALINA_HOME 环境变量未正确配置」问题而整理,包含以下内容: 1. **Apache Tomcat 9.0.69 官方安装包**:已验证兼容性,解压即用。 2. **环境变量配置指南**: - Windows 系统下 `CATALINA_HOME` 和 `JAVA_HOME` 的详细配置步骤。 - 常见错误排查方法(如路径含空格、未生效问题)。 3. **辅助工具脚本**:一键检测环境变量是否生效的批处理文件。 4. **解决方案文档**:图文并茂的 PDF 文档,涵盖从报错分析到成功启动的全流程。 适用场景: - Tomcat 9.x 版本环境配置 - Java Web 开发环境搭建 - 运维部署调试 注意事项: - 资源包路径需为纯英文,避免特殊字符。 - 建议使用 JDK 8 或更高版本。

    java毕业设计源码 仿360buy京东商城源码 京东JavaWeb项目源代码

    这是一款仿照京东商城的Java Web项目源码,完美复现了360buy的用户界面和购物流程,非常适合Java初学者和开发者进行学习与实践。通过这份源码,你将深入了解电商平台的架构设计和实现方法。欢迎大家下载体验,提升自己的编程能力!

    java-springboot+vue的乒乓球馆预约管理系统源码.zip

    系统选用B/S模式,后端应用springboot框架,前端应用vue框架, MySQL为后台数据库。 本系统基于java设计的各项功能,数据库服务器端采用了Mysql作为后台数据库,使Web与数据库紧密联系起来。 在设计过程中,充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。

    【javaweb毕业设计源码】大学生求职就业网

    这是一款专为大学生打造的求职就业网JavaWeb毕业设计源码,功能齐全,界面友好。它提供简历投递、职位搜索、在线交流等多种实用功能,能够帮助你顺利进入职场。无论你是想提升技术水平还是寻找灵感,这个源码都是不可多得的资源。快来下载,让你的求职之路更加顺畅吧!

    useTable(1).ts

    useTable(1).ts

Global site tag (gtag.js) - Google Analytics