我的编程空间,编程开发者的网络收藏夹
学习永远不晚

使用Runtime调用Process.waitfor导致的阻塞问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

使用Runtime调用Process.waitfor导致的阻塞问题

1. 关于Runtime类的小知识

  • Runtime.getRuntime()可以取得当前JVM的运行时环境,这也是在Java中唯一一个得到运行时环境的方法
  • Runtime中的exit方法是退出JVM

2. Runtime的几个重要的重载方法

方法名 作用
exec(String command); 在单独的进程中执行指定的字符串命令。
exec(String command, String[] envp) 在指定环境的单独进程中执行指定的字符串命令。
exec(String[] cmdarray, String[] envp, File dir) 在指定环境和工作目录的独立进程中执行指定的命令和变量
exec(String command, String[] envp, File dir) 在有指定环境和工作目录的独立进程中执行指定的字符串命令。

Runtime类的重要的方法还有很多,简单列举几个

  • exit(int status):终止当前正在运行的 Java 虚拟机
  • freeMemory():返回 Java 虚拟机中的空闲内存量。
  • load(String filename): 加载作为动态库的指定文件名。
  • loadLibrary(String libname): 加载具有指定库名的动态库。

3. Runtime的使用方式

错误的使用exitValue()


  public static void main(String[] args) throws IOException {
        String command = "ping www.baidu.com";
        Process process = Runtime.getRuntime().exec(command);
        int i = process.exitValue();
        System.out.println("字进程退出值:"+i);
    }

输出:

Exception in thread "main" java.lang.IllegalThreadStateException: process has not exited
at java.lang.ProcessImpl.exitValue(ProcessImpl.java:443)
at com.lirong.think.runtime.ProcessUtils.main(ProcessUtils.java:26)

原因:

exitValue()方法是非阻塞的,在调用这个方法时cmd命令并没有返回所以引起异常。阻塞形式的方法是waitFor,它会一直等待外部命令执行完毕,然后返回执行的结果。

修改后的版本:


  public static void main(String[] args) throws IOException {
        String command = "javac";
        Process process = Runtime.getRuntime().exec(command);
        process.waitFor();
        process.destroy();
        int i = process.exitValue();
        System.out.println("字进程退出值:"+i);
    }

此版本已然可以正常运行,但当主线程和子线程有很多交互的时候还是会出问题,会出现卡死的情况。

4. 卡死原因

  • 主进程中调用Runtime.exec会创建一个子进程,用于执行cmd命令。子进程创建后会和主进程分别独立运行。
  • 因为主进程需要等待脚本执行完成,然后对命令返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成。
  • 运行此cmd命令可以知道:子进程执行过程就是打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
  • 这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
  • 这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。

5. 解决方案

不断的读取消耗缓冲区的数据,以至子进程不会挂起,下面是具体代码:



@Slf4j
public class ProcessUtils {
    public static void main(String[] args) throws IOException, InterruptedException {
        String command = "ping www.baidu.com";
        Process process = Runtime.getRuntime().exec(command);
        readStreamInfo(process.getInputStream(), process.getErrorStream());
        int exit = process.waitFor();
        process.destroy();
        if (exit == 0) {
            log.debug("子进程正常完成");
        } else {
            log.debug("子进程异常结束");
        }
    }
    
    public static void readStreamInfo(InputStream... inputStreams){
        ExecutorService executorService = Executors.newFixedThreadPool(inputStreams.length);
        for (InputStream in : inputStreams) {
            executorService.execute(new MyThread (in));
        }
        executorService.shutdown();
    }
}

@Slf4j
public class MyThread implements Runnable {
    private InputStream in;
    public MyThread(InputStream in){
        this.in = in;
    }
    @Override
    public void run() {
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(in, "GBK"));
            String line = null;
            while((line = br.readLine())!=null){
                log.debug(" inputStream: " + line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

写到这里大家以为都结束了哇,并没有,哈哈哈,真实的生成环境总能给你带来很多神奇的问题,Runtime不仅可以直接调用CMD执行命令,还可以调用其他.exe程序执行命令。

所以纵使你读取了缓冲区的数据,你的程序依然可能会被卡死,因为有可能你的缓冲区根本就没有数据,而是你的.exe程序卡主了。嗯,所以为你以防万一,你还需要设置超时。

6. Runtime最优雅的调用方式



@Slf4j
public class ProcessUtils {
   
    public static int executeProcess(final long timeout, File fileDir, final String[] command)
            throws IOException, InterruptedException, TimeoutException {
        Process process = Runtime.getRuntime().exec(command, null, fileDir);
        Worker worker = new Worker(process);
        worker.start();
        try {
            worker.join(timeout);
            if (worker.exit != null){
                return worker.exit;
            } else{
                throw new TimeoutException();
            }
        } catch (InterruptedException ex) {
            worker.interrupt();
            Thread.currentThread().interrupt();
            throw ex;
        }
        finally {
            process.destroy();
        }
    }
    
    private static class Worker extends Thread {
        private final Process process;
        private Integer exit;
        private Worker(Process process) {
            this.process = process;
        }
        @Override
        public void run() {
            InputStream errorStream = null;
            InputStream inputStream = null;
            try {
                errorStream = process.getErrorStream();
                inputStream = process.getInputStream();
                readStreamInfo(errorStream, inputStream);
                exit = process.waitFor();
                process.destroy();
                if (exit == 0) {
                    log.debug("子进程正常完成");
                } else {
                    log.debug("子进程异常结束");
                }
            } catch (InterruptedException ignore) {
                return;
            }
        }
    }
    
    public static void readStreamInfo(InputStream... inputStreams){
        ExecutorService executorService = Executors.newFixedThreadPool(inputStreams.length);
        for (InputStream in : inputStreams) {
            executorService.execute(new MyThread(in));
        }
        executorService.shutdown();
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

使用Runtime调用Process.waitfor导致的阻塞问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何解决调用Process.waitfor导致的进程挂起问题

这篇文章主要介绍如何解决调用Process.waitfor导致的进程挂起问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!问题背景如果要在Java中调用shell脚本时,可以使用Runtime.exec或Proces
2023-06-22

python阻塞调度的使用方法

这篇文章主要介绍python阻塞调度的使用方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!python有哪些常用库python常用的库:1.requesuts;2.scrapy;3.pillow;4.twisted
2023-06-14

如何解决java调用process线程阻塞的问题

这篇文章将为大家详细讲解有关 如何解决java调用process线程阻塞的问题,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。java调用process线程阻塞问题项目需求中涉及java调用.bat文件进行
2023-06-15

Java中socket使用getInputStream()阻塞问题怎么解决

这篇文章主要介绍“Java中socket使用getInputStream()阻塞问题怎么解决”,在日常操作中,相信很多人在Java中socket使用getInputStream()阻塞问题怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简
2023-06-22

Java中常用阻塞队列的问题是什么

本篇内容主要讲解“Java中常用阻塞队列的问题是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中常用阻塞队列的问题是什么”吧!Java常用阻塞队列ArrayBlockingQueue
2023-06-29

使用lombok的@Data会导致栈溢出StackOverflowError问题

这篇文章主要介绍了使用lombok的@Data会导致栈溢出StackOverflowError问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-21

解决Go语言Websocket应用程序中的线程阻塞问题

解决Go语言Websocket应用程序中的线程阻塞问题在开发Web应用程序时,使用Websocket是一种非常常见和流行的方式。它可以建立持久的连接,并在服务器和客户端之间实时通信。然而,有时候我们可能会遇到线程阻塞的问题,这会导致应用程序
解决Go语言Websocket应用程序中的线程阻塞问题
2023-12-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录