`

java执行shell命令 outputStream缓冲区阻塞

    博客分类:
  • JAVA
 
阅读更多
http://bbs.csdn.net/topics/110150995
http://blog.csdn.net/jason20075563/article/details/6066563
http://www.cnblogs.com/yejg1212/archive/2013/06/02/3114242.html

创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。




Java调用其他程序时waitFor()阻塞
前段时间在工作中遇到这样一个问题,java代码中调用一个bat程序,在linux下执行完好,但是换到windows下就一直挂在那里不动了~

代码如下:

复制代码
public class CMDTest {
    public static void main(String[] args) {
        Process p = null;
        try {
            p = Runtime.getRuntime().exec("c:\\test.bat");

            p.waitFor();
            System.out.println(p.exitValue());
            System.out.println("over");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码
其中,test.bat里就是一些命令操作,在这里就写一个简单的ping命令。

ping www.baidu.com
上面的代码运行之后,等了半天都没反应。Process的api中有如下说明:

复制代码
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。

创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。
创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。
因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。
复制代码
也就是说:如果程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitFor()这里。

这就是问题所在!之后我查了下网上的解决办法,多数是创建两个线程在waitFor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。

按照这个思路,我写了如下util方法

复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class CommandUtil {
    // 保存进程的输入流信息
    private List<String> stdoutList = new ArrayList<String>();
    // 保存进程的错误流信息
    private List<String> erroroutList = new ArrayList<String>();

    public void executeCommand(String command) {
        // 先清空
        stdoutList.clear();
        erroroutList.clear();

        Process p = null;
        try {
            p = Runtime.getRuntime().exec(command);

            // 创建2个线程,分别读取输入流缓冲区和错误流缓冲区
            ThreadUtil stdoutUtil = new ThreadUtil(p.getInputStream(), stdoutList);
            ThreadUtil erroroutUtil = new ThreadUtil(p.getErrorStream(), erroroutList);
            //启动线程读取缓冲区数据
            stdoutUtil.start();
            erroroutUtil.start();

            p.waitFor();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public List<String> getStdoutList() {
        return stdoutList;
    }

    public List<String> getErroroutList() {
        return erroroutList;
    }

}

class ThreadUtil implements Runnable {
    // 设置读取的字符编码
    private String character = "GB2312";
    private List<String> list;
    private InputStream inputStream;

    public ThreadUtil(InputStream inputStream, List<String> list) {
        this.inputStream = inputStream;
        this.list = list;
    }

    public void start() {
        Thread thread = new Thread(this);
        thread.setDaemon(true);//将其设置为守护线程
        thread.start();
    }

    public void run() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(inputStream, character));
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line != null) {
                    list.add(line);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //释放资源
                inputStream.close();
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
复制代码
再整个方法测试下:

复制代码
import java.util.List;

public class TestMain {
    public static void main(String[] args) {
        CommandUtil util = new CommandUtil();
        util.executeCommand("c:\\test.bat");
        printList(util.getStdoutList());
        System.out.println("--------------------");
        printList(util.getErroroutList());
    }

   
    public static void printList(List<String> list){
        for (String string : list) {
            System.out.println(string);
        }
    }
   
}
复制代码
这样一来,问题确实解决了,再也不会出现阻塞了~

分享到:
评论

相关推荐

    java调用shell脚本

    在IT行业中,尤其是在服务器管理和自动化任务执行的场景下,经常需要使用编程语言调用操作系统级别的命令,例如Shell脚本。Java作为一种跨平台的编程语言,提供了多种方式来调用Shell脚本,实现与操作系统的交互。...

    Java 调用 Shell 命令

    2. **调用Shell脚本**:通过Java的`Runtime.getRuntime().exec()`方法来执行Shell命令或脚本。 下面是一个具体的Java类实现示例,用于调用Shell脚本并记录执行日志: ```java import java.io.*; import java.text....

    利用缓冲区提高Java应用程序的IO性能

    ### 利用缓冲区提高Java应用程序的IO性能 #### 摘要与背景介绍 Java作为一门具有跨平台特性的编程语言,在多个领域都获得了广泛的应用。随着Java应用的不断扩展,其性能问题逐渐成为人们关注的重点,尤其是输入...

    (接上篇博客)Java SSH远程执行Shell脚本实现

    在本篇博客中,我们将探讨如何使用Java通过SSH远程执行Shell脚本,这对于系统管理和自动化任务非常有用。我们将重点关注`ExecuteRemoteShell.java`这个文件,它应该包含了实现这一功能的核心代码。 首先,为了实现...

    java 远程调用Shell脚本客户端包

    3. **Shell脚本执行**:在SSH连接建立后,Java程序可以通过执行`exec`命令来运行远程服务器上的Shell脚本。脚本可以包含任意的Linux或Unix命令,甚至复杂的流程控制语句。 4. **输入/输出流处理**:为了获取Shell...

    java控制台输入cmd命令, ssh远程linux shell命令, 并打印命令输出到控制台

    在Java编程中,有时我们需要与操作系统进行交互,执行系统级别的命令,例如在控制台执行CMD命令或者通过SSH连接远程Linux服务器执行shell命令。这在自动化脚本、系统管理或者远程监控等场景中非常常见。本篇文章将...

    java Sokcet远程调用shell

    Java Socket远程调用Shell是一种通过Java程序利用Socket通信协议实现对远程服务器的Shell命令执行的技术。这种方式常用于系统管理、自动化运维或者分布式系统的组件交互。下面将详细介绍这一技术的实现原理、步骤...

    java远程调shell

    Java远程调用Shell是一种技术,它允许通过Java程序执行远程服务器上的操作系统命令,这在系统集成、自动化运维或跨平台任务调度中非常有用。这里,我们深入探讨如何实现这一功能,以及涉及的关键知识点。 首先,...

    Java开发的SHELL CRaSH

    Java开发的SHELL CRaSH是指在Java编程环境中,开发者可能会遇到的一种运行时错误或异常情况,通常与命令行交互、脚本执行或者系统资源管理有关。在这个话题中,我们将深入探讨Java如何与Shell交互,可能导致CRaSH的...

    JSCH执行命令是输入密码的方法

    在IT领域,JavaScript Secure Channel (JSCH) 是一个非常实用的Java库,它允许开发者在Java应用程序中实现Secure Shell (SSH) 协议。本文将深入探讨如何使用JSCH库来执行远程命令,并且处理输入密码的情况。我们将...

    Java调用远程Shell脚本

    综上所述,Java调用远程Shell脚本涉及的主要知识点包括:SSH协议,Ganymed SSH-2库的使用,建立SSH连接,执行远程命令,处理输出,以及参数的动态配置。在实际应用中,这些技术可以帮助开发者实现远程服务器的自动化...

    InputStream与OutputStream及File间互转

    这可以通过创建一个临时缓冲区实现,例如使用`BufferedInputStream`和`BufferedOutputStream`,或者使用`IOUtils`类(来自Apache Commons IO库): ```java // 使用Buffered streams BufferedInputStream in = ...

    在Linux系统下用java执行系统命令实例讲解

    在Linux系统下,Java编程语言提供了丰富的API来执行操作系统级别的命令。这主要通过`java.lang.Runtime`类和`java.lang.ProcessBuilder`类实现。本文将深入讲解如何在Java程序中调用Linux命令,并通过实例来阐述这一...

    java 文件存储 Inputstream outputstream reader writer的用法

    `InputStream`、`OutputStream`、`Reader`和`Writer`是Java IO流的基础类,它们提供了读写文件的基本接口。本篇文章将深入讲解这些类的用法,并提供实际的代码示例,帮助初学者更好地理解和应用。 1. `InputStream`...

    Java中OutputStream类数据操作方法.pdf

    Java中OutputStream类数据操作方法.pdf 学习资料 复习资料 教学资源

    java.lang.IllegalStateException: OutputStream already obtain

    当你试图写入数据到某个目的地(如文件、网络连接或内存缓冲区)时,你需要创建一个OutputStream的实例。然而,一旦一个OutputStream被一个线程获取并开始使用,其他线程再尝试获取它就会抛出IllegalStateException...

    java阻塞i/o与非阻塞i/o控制

    1. **Buffer**:用于在Java应用和操作系统之间存储数据的缓冲区。 2. **Channel**:连接到数据源(如文件、套接字)的通道,可以读写数据。 3. **Selector**:监控多个Channel,当某个Channel准备就绪时,Selector会...

    linux shell的java操作实现

    除了基础的命令执行,还可以实现更复杂的Shell脚本执行,例如通过`channel.setCommand("bash your_script.sh")`来执行存储在远程服务器上的Shell脚本。同时,还可以使用JSch的SCP或SFTP功能进行文件的上传和下载。 ...

    java执行ssh命令

    在Java编程中,执行SSH(Secure Shell)命令是一项常见的任务,尤其在远程服务器管理、自动化运维和分布式系统中。SSH是一种网络协议,用于安全地在远程主机上执行命令和传输数据。下面我们将深入探讨如何在Java中...

    将输出流OutputStream转化为输入流InputStream的方法

    在Java编程中,有时我们可能需要将一个已经写入数据的`OutputStream`转换为`InputStream`,以便重新读取这些数据。这种情况通常出现在临时存储或处理数据时,例如在网络传输或者存储到内存中的场景。本篇文章将深入...

Global site tag (gtag.js) - Google Analytics