一:在使用Java的Process类时,调用waitFor方法可能会导致线程阻塞无法返回,例如,解压压缩包时,压缩包有密码,而命令中没有设置密码
public void processExec(String command) {
Process ps = Runtime.getRuntime().exec(command);
ps.waitFor();
...
...
}
二:这种情况是因为:
主进程中调用Runtime.exec会创建一个子进程,用于执行cmd命令。子进程创建后会和主进程分别独立运行。
因为主进程需要等待脚本执行完成,然后对命令返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成
运行此cmd命令可以知道:子进程执行过程就是打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。
三:这种时候需要添加一个工具类或者配置类,不断的读取消耗缓冲区的数据,以至子进程不会挂起,并且设置超时
ProcessConfig配置类,start方法通过线程池创建线程
@Slf4j
@Data
public class ProcessConfig implements Runnable {
private static final int SCHEDULE_SIZE = Runtime.getRuntime().availableProcessors();
private ScheduledExecutorService scheduledExecutorService;
private String character = "utf-8";
private InputStream inputStream;
private String errStr;
public String getErrStr() {
return errStr;
}
public void setErrStr(String errStr) {
this.errStr = errStr;
}
public ProcessConfig(InputStream inputStream) {
this.inputStream = inputStream;
scheduledExecutorService = null;
}
/**
* 以utf-8的编码格式去读取文件
*/
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(inputStream, character));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
errStr = sb.toString();
} catch (UnsupportedEncodingException e) {
log.error("执行解IO操作异常!" + e);
} catch (IOException e) {
log.error("执行解IO操作异常!" + e);
}finally{
if (br != null) {
try {
br.close();
} catch (IOException e) {
log.error("执行解IO操作异常!" + e);
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error("执行解IO操作异常!" + e);
}
}
}
}
public void start() {
scheduledExecutorService = new ScheduledThreadPoolExecutor(SCHEDULE_SIZE,
r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.setName("");
return thread;
});
}
}
原来添加代码异常处理
public void processExec(String command) {
Process ps = null;
try {
ps = Runtime.getRuntime().exec(command);
ProcessConfig pu = new ProcessConfig(ps.getErrorStream());
ps.getOutputStream().close();
ps.waitFor();
String errorStr = pu.getErrStr();
if (errorStr != null) {
log.error("执行解压操作异常!" + errorStr);
}
} catch (IOException e) {
log.error("执行解IO操作异常!" + e);
} catch (InterruptedException e) {
log.error("执行解压打断异常!" + e);
} finally {
if (ps != null) {
ps.destroy();
}
}
}