关于Process的waitFor死锁问题及解决方案

目录
  • Process的waitFor死锁问题
    • 1、发现问题
    • 2、原因查找
    • 3、造成死锁原理图
      • 问题代码
      • 解决方案
  • Java中死锁的简单例子及其避免
    • 我们来看一个死锁的简单例子
    • 那么要怎么预防死锁呢?
      • 下面介绍几个常见方法:

Process的waitFor死锁问题

1、发现问题

在实际开发中,进行文件操作时,使用Process对文件进行解压操作,程序执行过程中会出现一直卡在那的问题。

2、原因查找

问题在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。接着来分析缓冲区,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。

程序运行过程产生等待分析:

(1)、当我们使用Runtime.exec执行命令时,JAVA的线程会创建一个子进程,用于执行命令,而且子进程和JAVA线程会分别独立运行。

(2)、JAVA线程需要等待命令的执行完成,对命令的日志和返回值进行处理,所以在JAVA线程中调用Process.waitFor挂起来等待子进程完成。

(3)、子线程执行时,不断的打印日志信息,我们通过Process.getInputStream和Process.getErrorStream进行获取正常输出日志和错误日志进行处理,也即是读数据。

(4)、这个时候子进程不断的向JAVA线程写入数据,而JAVA线程调用Process.waitFor后已经阻塞挂起,而子进程在不断的向JAVA线程进行写入数据,当我们的Process.getInputStream的buffer缓冲区被写满,JAVA线程依然挂起并未消费buffer中的数据,导致子进程无法继续向buffer缓冲区中继续写入数据,导致子进程也挂起。

(5)、这个时候JAVA线程和子进程都处于挂起的状态,JAVA线程等待子进程的结束,子进程等待JAVA线程对buffer缓冲区中的数据进行消费。两者在相互等待导致死锁。

3、造成死锁原理图

问题代码

public static String cmd(String cmd) throws InterruptedException  {
        try {
                // 使用Runtime来执行command,生成Process对象
                Process process = Runtime.getRuntime().exec(
                        new String[] { "/bin/sh", "-c", cmd });
                int exitCode = process.waitFor();
                // 取得命令结果的输出流
                InputStream is = process.getInputStream();
                // 用一个读输出流类去读
                InputStreamReader isr = new InputStreamReader(is);
                // 用缓冲器读行
                BufferedReader br = new BufferedReader(isr);
                String line = null;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                is.close();
                isr.close();
                br.close();

                return sb.toString();
            } catch (java.lang.NullPointerException e) {

            	throw new BaseException(e);
            } catch (java.io.IOException e) {
            	throw new BaseException(e);
            }
            return null;
    }

这段代码会产生死锁的原因是, BufferedReader br = new BufferedReader(isr);放在了process.waitFor();之后执行,并不能有效的清空缓冲区,还是会造成缓冲区满,造成阻塞。

解决方案

知道问题原因就号解决了

public static String callCmd(String cmd) throws InterruptedException  {
         Process process = null;
         BufferedReader normalReader = null;
         BufferedReader errorReader = null;
         try {
                process = Runtime.getRuntime().exec(
                        new String[] { "/bin/sh", "-c", cmd });
                /*
                    启动读取buffer缓冲区子线程,避免buffer缓冲区被写满,导致线程等待问题
                 */
                normalReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                errorReader = new  BufferedReader(new  InputStreamReader(process.getErrorStream()));
                String line = null;
                StringBuilder sb = new StringBuilder();
                while ((line = normalReader.readLine()) != null) {
                    sb.append(line);
                }
                String errorLine;
                while ((errorLine = errorReader.readLine()) != null) {
                    logger.warn("脚本文件执行信息ErrorStream:{}", errorLine);
                }
                // 等待程序执行结束并输出状态
                int exitCode = process.waitFor();
                if (0 == exitCode) {
                    logger.info("脚本文件执行成功!");
                } else {
                    logger.error("脚本文件执行失败:{}",cmd);
                    throw new BaseException("脚本文件执行失败:"+cmd);
                }
                return sb.toString();
          } catch (java.lang.NullPointerException e) {
                throw new BaseException(e);
          } catch (java.io.IOException e) {
                throw new BaseException(e);
          }finally{
                if (null != normalReader) {
                    try {
                        normalReader.close();
                    } catch (IOException e) {
                        logger.error("流文件关闭异常:", e);
                    }
                }
                if (null != errorReader) {
                    try {
                        errorReader.close();
                    } catch (IOException e) {
                        logger.error("流文件关闭异常:", e);
                    }
                }
                if (null != process) {
                    process.destroy();
                }
         }
    }

Java中死锁的简单例子及其避免

死锁:当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞。

比如,线程1已经持有了A锁并想要获得B锁的同时,线程2持有B锁并尝试获取A锁,那么这两个线程将永远地等待下去。

我们来看一个死锁的简单例子

public class DeadLockTest
{
    private static Object A = new Object(), B = new Object();
    public static void main(String[] args)
    {
        new Thread(() -> {
            System.out.println("线程1开始执行...");
            synchronized (A)
            {
                try
                {
                    System.out.println("线程1拿到A锁");
                    //休眠两秒让线程2有时间拿到B锁
                    Thread.sleep(2000);
                } catch (Exception e)
                {
                    e.printStackTrace();
                }
                synchronized (B)
                {
                    System.out.println("线程1拿到B锁");
                }
            }
        }).start();
        new Thread(() -> {
            System.out.println("线程2开始执行...");
            synchronized (B)
            {
                try
                {
                    System.out.println("线程2拿到B锁");
                    //休眠两秒让线程1有时间拿到A锁
                    Thread.sleep(2000);
                } catch (Exception e)
                {
                    e.printStackTrace();
                }
                synchronized (A)
                {
                    System.out.println("线程2拿到A锁");
                }
            }
        }).start();
    }
}

运行结果:

从运行结果可看到,线程1拿到了A锁,并尝试去获取B锁,与此同时线程2拿到了B锁并尝试去获取A锁,此时线程1和线程2就陷入了无限的等待,形成死锁。

那么要怎么预防死锁呢?

下面介绍几个常见方法:

1、避免一个线程同时获取多个锁

2、避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源

3、尝试使用定时锁,使用lock.tryLock来代替使用内置锁。

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

(0)

相关推荐

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

    java调用process线程阻塞问题 项目需求中涉及java调用.bat文件进行图像处理,先直接上简略版程序 public void draw(){ //调用bat脚本进行图像处理 Process process = null; InputStream in = null; try { process = Runtime.getRuntime().exec("startup.bat"); //输出测试 // in = process.getInputStream(); // Stri

  • Java Process.waitFor()方法详解

    目录 Java Process.waitFor()方法 描述 声明 参数 返回值 异常 实例 process.waitFor()没有作用 Java Process.waitFor()方法 Process.waitFor()方法 将导致当前线程等待,直到该对象的进程结束,才返回调用. 描述 java.lang.Process.waitFor()方法将导致当前的线程等待,如果必要的话,直到由该Process对象表示的进程已经终止.此方法将立即返回,如果子进程已经终止.如果子进程尚未终止,则调用线程将

  • 解决Process.getInputStream()阻塞的问题

    Process.getInputStream()阻塞问题 Java中 Runtime.getInstance().exec (String cmd) 或者 new ProcessBuilder(String cmd).start() 都可以产生子进程对象Process.通过调用Process对象的waitFor()方法可以使主进程进入等待状态,直至子进程执行完毕,再进行下一步工作.如果对子进程处理不当,有可能造成主进程阻塞,整个程序死掉. java Api中关于Process说的是: Proce

  • 基于Process#waitFor()阻塞问题的解决

    目录 Process#waitFor()阻塞问题 Process.waitFor()导致主线程堵塞 Process#waitFor()阻塞问题 有时需要在程序中调用可执行程序或脚本命令: Process process = Runtime.getRuntime().exec(shPath); int exitCode = process .waitFor(); Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执

  • 关于Process的waitFor死锁问题及解决方案

    目录 Process的waitFor死锁问题 1.发现问题 2.原因查找 3.造成死锁原理图 问题代码 解决方案 Java中死锁的简单例子及其避免 我们来看一个死锁的简单例子 那么要怎么预防死锁呢? 下面介绍几个常见方法: Process的waitFor死锁问题 1.发现问题 在实际开发中,进行文件操作时,使用Process对文件进行解压操作,程序执行过程中会出现一直卡在那的问题. 2.原因查找 问题在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞.接着来

  • java使用Process调用exe程序及Process.waitFor()死锁问题解决

    目录 前言 文章参考 1. 使用process调用exe程序 2. waitfor 问题描述分析 3. 死锁问题解决 总结 前言 最近在开发android的同时也在开发java ,碰到了需要使用java 程序调用exe的需求,这里我使用的 process 来调用的.该篇文章 读完需要8+分钟,文章类型为 小白入门类型,此处主要记录,方便以后学习补充… 如有不正确的地方还望海涵 及 指出…. 文章参考 process参考 waitfor挂起解析 1. 使用process调用exe程序 Proces

  • Java Process中waitFor()的问题详解

    目录 总结 在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本.在Java中提供了两种方法来启动其他程序: (1) 使用Runtime的exec()方法 (2) 使用ProcessBuilder的start()方法 .Runtime和ProcessBulider提供了不同的方式来启动程序,设置启动参数.环境变量和工作目录.但是这两种方法都会返回一个用于管理操作系统进程的Process对象.这个对象中的waitFor()是我们今天要讨论的重点. 来说说我遇到的实际

  • python 多线程死锁问题的解决方案

    死锁的原理非常简单,用一句话就可以描述完.就是当多线程访问多个锁的时候,不同的锁被不同的线程持有,它们都在等待其他线程释放出锁来,于是便陷入了永久等待.比如A线程持有1号锁,等待2号锁,B线程持有2号锁等待1号锁,那么它们永远也等不到执行的那天,这种情况就叫做死锁. 关于死锁有一个著名的问题叫做哲学家就餐问题,有5个哲学家围坐在一起,他们每个人需要拿到两个叉子才可以吃饭.如果他们同时拿起自己左手边的叉子,那么就会永远等待右手边的叉子释放出来.这样就陷入了永久等待,于是这些哲学家都会饿死. 这是一

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

    目录 1. 关于Runtime类的小知识 2. Runtime的几个重要的重载方法 3. Runtime的使用方式 4. 卡死原因 5. 解决方案 6. Runtime最优雅的调用方式 1. 关于Runtime类的小知识 Runtime.getRuntime()可以取得当前JVM的运行时环境,这也是在Java中唯一一个得到运行时环境的方法 Runtime中的exit方法是退出JVM 2. Runtime的几个重要的重载方法 方法名 作用 exec(String command); 在单独的进程中

  • Java 死锁解决方案顺序锁和轮询锁

    目录 死锁解决方案分析 解决方案1:顺序锁 解决方案2:轮询锁 总结 前言: 死锁(Dead Lock)指的是两个或两个以上的运算单元(进程.线程或协程),都在等待对方停止执行,以取得系统资源,但是没有一方提前退出,就称为死锁. 死锁示例代码如下: public class DeadLockExample { public static void main(String[] args) { Object lockA = new Object(); // 创建锁 A Object lockB =

  • Python中死锁的形成示例及死锁情况的防止

    死锁示例 搞多线程的经常会遇到死锁的问题,学习操作系统的时候会讲到死锁相关的东西,我们用Python直观的演示一下. 死锁的一个原因是互斥锁.假设银行系统中,用户a试图转账100块给用户b,与此同时用户b试图转账200块给用户a,则可能产生死锁. 2个线程互相等待对方的锁,互相占用着资源不释放. #coding=utf-8 import time import threading class Account: def __init__(self, _id, balance, lock): sel

  • SQL Server并发处理存在就更新解决方案探讨

    前言 本节我们来讲讲并发中最常见的情况存在即更新,在并发中若未存在行记录则插入,此时未处理好极容易出现插入重复键情况,本文我们来介绍对并发中存在就更新行记录的七种方案并且我们来综合分析最合适的解决方案. 探讨存在就更新七种方案 首先我们来创建测试表 IF OBJECT_ID('Test') IS NOT NULL DROP TABLE Test CREATE TABLE Test ( Id int, Name nchar(100), [Counter] int,primary key (Id),

  • Python 防止死锁的方法

    问题 你正在写一个多线程程序,其中线程需要一次获取多个锁,此时如何避免死锁问题. 解决方案 在多线程程序中,死锁问题很大一部分是由于线程同时获取多个锁造成的.举个例子:一个线程获取了第一个锁,然后在获取第二个锁的 时候发生阻塞,那么这个线程就可能阻塞其他线程的执行,从而导致整个程序假死. 解决死锁问题的一种方案是为程序中的每一个锁分配一个唯一的id,然后只允许按照升序规则来使用多个锁,这个规则使用上下文管理器 是非常容易实现的,示例如下: import threading from contex

随机推荐