ProcessBuilder pb = new ProcessBuilder("C:\Debug\TestRedis.exe", keyNmae); pb.redirectErrorStream(true); Process process = pb.start(); //可能导致进程阻塞,甚至死锁 int ret = process.waitFor();
1、waitFor问题描述分析
1、 主进程中调用pb.start会创建一个子进程,用于执行shell /exe 脚本。子进程创建后会和主进程分别独立运行。
2.、因为主进程需要等待脚本执行完成,然后对脚本返回值或输出进行处理,所以这里主进程调用process.waitFor()等待子进程完成。
3.、子进程执行过程就是不断的打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
4.、这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
5.、这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。
2、解决死锁问题
基于上述分析,只要主进程在waitFor之前,能不断处理缓冲区中的数据就可以。因为,我们可以再waitfor之前,单独启2个额外的线程,分别用于处理InputStream和ErrorStream就可以
try ( InputStream inputStream = process.getInputStream(); InputStream inputErrorStream = process.getErrorStream(); ) { // 读取Shell的输出内容,并添加到string中: //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流 new Thread(() -> { try { val msg = IOUtils.toString(inputStream, StandardCharsets.UTF_8); response.setShellOutput(msg); } catch (Exception e) { log.error(e.getMessage(), e); } }).start(); new Thread(() -> { try { var errMsg = IOUtils.toString(inputErrorStream, StandardCharsets.UTF_8); response.setShellErrorOutput(errMsg); } catch (Exception e) { log.error(e.getMessage(), e); } }).start(); process.waitFor(2, TimeUnit.HOURS);////可能导致进程阻塞,甚至死锁 int exitCode = process.exitValue();// response.setSuccessful(exitCode == SHELL_EXIT_CODE_SUCCESS); }