Java调用外部程序技巧

http://www.yankay.com/java%E8%B0%83%E7%94%A8%E5%A4%96%E9%83%A8%E7%A8%8B%E5%BA%8F%E6%8A%80%E5%B7%A7/


Process process = Runtime.getRuntime().exec(cmd);
process.waitfor();



如果直接在Shell中调用这个程序,程序会很快结束,不会僵死。

为什么会堵塞呢,原因是当调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立3个管道连接,标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取,数据会暂时缓冲在Linux的缓冲区,缓冲区满后该程序将无法继续写数据,会僵死,所以Java程序就会僵死在waitfor(),永远无法结束。

解决办法就是增加两个线程,一个线程负责读标准输出流,另一个负责读标准错误流,这样子数据就不会积压在缓冲区,程序就能够顺利运行。



import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*java调用外部命令处理.
*/
public class ProcessUtil {
private static final int DEFAULT_BUFFER_SIZE = 1024;
private static Log log = LogFactory.getLog(ProcessUtil.class);

private InputStream in;

private OutputStream out;
private OutputStream err;

public ProcessUtil(OutputStream out, OutputStream err, InputStream in) {
if (out == null) {
out = new NullOutputStream();
}
if (err == null) {
err = new NullOutputStream();
}
this.out = out;
this.err = err;
this.in = in;

}

public int process(String cmd, String[] envp, File dir) throws IOException,
InterruptedException {
Process p = Runtime.getRuntime().exec(cmd, envp, dir);
return process(p);
}

public int process(String[] cmdarray, String[] envp, File dir)
throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec(cmdarray, envp, dir);
return process(p);
}

private int process(Process p) throws IOException, InterruptedException {
try {
OutputStream pin = p.getOutputStream();

StreamGobbler outg = new StreamGobbler(p.getInputStream(), out);
StreamGobbler errg = new StreamGobbler(p.getErrorStream(), err);

outg.start();
errg.start();

if (in != null) {
byte[] inBuf = new byte[DEFAULT_BUFFER_SIZE];
int inN = 0;
while (-1 != (inN = in.read(inBuf))) {
pin.write(inBuf, 0, inN);
}
pin.flush();
}

int code = p.waitFor();
outg.join();
errg.join();
return code;
} finally {
closeQuietly(p.getOutputStream());
closeQuietly(p.getInputStream());
closeQuietly(p.getErrorStream());
}

}

private void closeQuietly(Closeable closeable) {
try {
if (closeable != null)
closeable.close();
} catch (Exception e) {
log.warn("close error", e);
}
}

}

class StreamGobbler implements Runnable {
private static final int DEFAULT_BUFFER_SIZE = 1024;
private static Log log = LogFactory.getLog(StreamGobbler.class);

private InputStream is;
private OutputStream os;
private Thread thread;

public StreamGobbler(InputStream is, OutputStream os) {
this.is = is;
this.os = os;
}

public void start() {
thread = new Thread(this);
thread.setDaemon(true);
thread.start();
}

public void run() {
try {
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
int n = 0;
while (-1 != (n = is.read(buf))) {
os.write(buf, 0, n);
}
os.flush();
} catch (Exception ex) {
log.warn("stream error", ex);
}
}

public void join() throws InterruptedException {
thread.join();
}

}

class NullOutputStream extends OutputStream {

public static final NullOutputStream NULL_OUTPUT_STREAM = new NullOutputStream();

@Override
public void write(byte[] b, int off, int len) {
}

@Override
public void write(int b) {
}

@Override
public void write(byte[] b) throws IOException {
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值