[Java]_[初级]_[如何调用外部命令获取输出并设置它的超时退出]
Posted infoworld
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java]_[初级]_[如何调用外部命令获取输出并设置它的超时退出]相关的知识,希望对你有一定的参考价值。
场景
-
在开发
Java
程序的时候,有时候需要执行外部命令以获取特定信息,但是外部命令执行可能由于某些原因(比如CPU,内存占用高后延迟执行)比正常执行的时间要长,有时候甚至挂起阻塞了Java
线程的执行。那么我们如何使用标准库来完成调用外部命令的超时时间设置? -
还有基本的问题,如何读取启动进程的输出?
说明
Java
使用ProcessBuilder
来创建外部命令的进程,并使用方法command
来异步执行外部命令.
ProcessBuilder ps = new ProcessBuilder();
ps.command(命令路径);
Process process = ps.start();
- 之后通过对象
process
的process.getInputStream()
获取一个连接到进程输出的输入流,这个输入流使用管道的方式连接到进程的标准输出上,所以读取这个输入流就可以读取这个进程的输出。通过读取这个进程的输出并存储到StringBuffer
里.
StringBuffer line = new StringBuffer();
String temp = "";
try(BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())))
while ((temp = in.readLine()) != null)
line.append(temp);
exitCode = process.waitFor();
catch (Exception e)
e.printStackTrace();
finally
if(exitCode != 0)
process.destroy();
- 为了外部进程的超时判断,那么外部进程肯定需要额外的线程进行运行和读取输出,使用了线程池
ThreadPoolExecutor
来执行任务会比较好, 避免频繁的创建线程。而Executor.submit
返回了一个Future
对象,这个对象是并发包里的类,主要是可以异步获取工作线程返回的值。通过这个Future
对象可以获取到外部进程执行的结果,它还有设置延时等待的get
方法正好合适。
String java.util.concurrent.Future.get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
- 在使用
Future
的get
获取返回结果时,可以通过抛出的超时异常来销毁未结束的进程。关于Future
的介绍可以参考课程JDK8-Java8-JavaSE8新特性和增强功能.
TaskCallable callable = new TaskCallable();
Future<String> handler = executor.submit(callable);
String message = "";
try
message = handler.get(kGenWaitTimeoutSeconds, TimeUnit.SECONDS);
catch (InterruptedException e)
e.printStackTrace();
catch (ExecutionException e)
e.printStackTrace();
catch (TimeoutException e)
callable.cancelProcess();
- 在以下的例子里我把调用外部命令封装到一个
Callable<String>
对象里,方便Executor.submit
调用.
例子
package com;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.jupiter.api.Test;
public class ProcessExecTest
private ThreadPoolExecutor executor;
private BlockingQueue<Runnable>queue;
private static int queueSize = 1024;
private static int threadKeepAliveTime = 120;
private static int kGenWaitTimeoutSeconds = 10;
public static void print(String key,String str)
System.out.println(key+": "+str);
public ProcessExecTest()
int processors = Runtime.getRuntime().availableProcessors();
// 常规任务
queue = new LinkedBlockingQueue<Runnable>(queueSize);
executor = new ThreadPoolExecutor(processors,processors,
threadKeepAliveTime,TimeUnit.SECONDS, queue);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
@Test
public void testProcess()
TaskCallable callable = new TaskCallable();
Future<String> handler = executor.submit(callable);
String message = "";
try
message = handler.get(kGenWaitTimeoutSeconds, TimeUnit.SECONDS);
catch (InterruptedException e)
e.printStackTrace();
catch (ExecutionException e)
e.printStackTrace();
catch (TimeoutException e)
callable.cancelProcess();
print("message",message);
public static class TaskCallable implements Callable<String>
private Process process;
private int exitCode = 1;
public void cancelProcess()
if(process != null && exitCode != 0)
process.destroy();
public TaskCallable()
@Override
public String call() throws Exception
File dir = new File("C:\\\\Windows\\\\System32");
ProcessBuilder ps = new ProcessBuilder();
ps.directory(dir);
Path dirPath = dir.toPath();
Path exePath = dirPath.resolve("cmd.exe");
ps.command(exePath.toString(),"/C","echo tobey");
process = ps.start();
if(process == null)
return "";
StringBuffer line = new StringBuffer();
String temp = "";
try(BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())))
while ((temp = in.readLine()) != null)
line.append(temp);
exitCode = process.waitFor();
catch (Exception e)
e.printStackTrace();
finally
if(exitCode != 0)
process.destroy();
return (exitCode == 0)?line.toString():"";
输出
message: tobey
参考
以上是关于[Java]_[初级]_[如何调用外部命令获取输出并设置它的超时退出]的主要内容,如果未能解决你的问题,请参考以下文章
[Android]_[初级]_[AndroidStudio编译输出中文乱码]
01_JNI是什么,为什么使用,怎么用JNI,Cygwin环境变量配置,NDK案例(使用Java调用C代码),javah命令使用