正常来说java调用shell命令就是用
String[] cmdAry = new String[]{"/bin/bash","-c",cmd}
Runtime.getRuntime().exec(cmdAry);
实际上就是创建个子进程去执行这个命令。
问题来了: 如果我执行下面这句命令
exp system/manager buffer=64000 file=/home/oracle/beifen/beifen.dmp FULL=Y
这种输出日志量特别大的命令,就会出现卡死,一直不动,后来了解到 子进程产生的输出日志都会写到窗口里,因为窗口有缓存大小限制,
到达一定程度时就会卡死,导致死锁,后来百度了其他作者的资料才发现,这种直接调用exec用法是不好的,数据量小可能体现不出来,
数据量大的时候 就会直接缓存爆满 子进程就会卡死,所以我们可以创建2个线程来消费子进程运行shell命令产生的日志,正确用法如下
public class TestController { @GetMapping("/exp") public void addHomePage(String cmd) throws InterruptedException, IOException { //创建子进程调用shell命令 new MyThread(cmd).start(); } } class MyThread extends Thread { String cmd; MyThread(String cmd) { this.cmd = cmd; }
public void run(){ Process proc = null; try {
String[] cmdAry = new String[]{"/bin/bash","-c",cmd} proc = Runtime.getRuntime().exec(cmdAry); } catch (IOException e) { e.printStackTrace(); } //接收子进程的汇报信息和错误信息,避免阻塞 new StreamGobbler(proc.getInputStream(),"INFO").start(); new StreamGobbler(proc.getErrorStream(),"ERROR").start(); //获取子进程执行结果状态 int status= 0; try { status = proc.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } if (status == 0){ System.out.println("执行完毕"); }else System.out.println("执行失败"); //销毁子进程 proc.destroy(); } } 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(); } } }
这样就可以了 记得以后调用shell命令都用这种形式