您的位置:首页 > 编程语言 > Java开发

Java Process 阻塞测试总结

2018-01-20 14:44 211 查看

Java Process 阻塞测试总结

Process阻塞原因:输入流和错误流分开的,没有处理,就会发生阻塞,归根结底本质上是bio引起的io阻塞问题。

getInputStream,getErrorSteam就是获取脚本或者命令的控制台回显信息,前者获取的是标准输出的回显信息,后者获取的是标准错误的回显信息

Process原理:使用Runtime.getRuntime().exec(cmd)会在当前进程建立一个子进程,子进程由于没有控制台,它的标准输出和标准错误就会返回给父进程Process,因此通过getInputStream和getErrorStream就可以获取到这些信息。

测试代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class JavaExeBat {

public JavaExeBat() {

}

public static void main(String[] args) {
Process p;
//test.bat中的命令是ipconfig/all
String cmd="sh test.sh ";
//String cmd="ping 127.0.0.1 -c 4";

try {
//执行命令
p = Runtime.getRuntime().exec(cmd);
//取得命令结果的输出流
//输出流
InputStream fis=p.getInputStream();
//错误流
InputStream ferrs=p.getErrorStream();
//用一个读输出流类去读
InputStreamReader isr=new InputStreamReader(fis);
InputStreamReader errsr=new InputStreamReader(ferrs);
//用缓冲器读行
BufferedReader br=new BufferedReader(isr);
BufferedReader errbr=new BufferedReader(errsr);
String line=null;
String lineerr = null;
//直到读完为止
while((line=br.readLine())!=null) {
//有可能发生阻塞的问题
System.out.println("return input Str:" + line);
}
while((lineerr=errbr.readLine())!=null){
//有可能发生阻塞的问题
System.out.println("return err Str:" + lineerr);
}
int exitVal = p.waitFor();
System.out.println("exitVal:" + exitVal);
} catch (Exception e) {
e.printStackTrace();
}
}
}

test.sh如下

#!/bin/bash

for((i=0; i < 100000; i++));do
//输出的标准输出
echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
//输出到标准错误
echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1>&2
done

经过测试发现,如果JavaExeBat.java文件中只开启标准输出或者标准错误时,进程就会夯住,无法通过waiteFor获取其返回值,因为脚本中分别输出了100000w条信息到标准输出和标准错误,而下述代码只处理了getInputStream,导致标准错误输出流的信息太多返回给当前进程,没有得到处理,因此阻塞。代码如下:

p = Runtime.getRuntime().exec(cmd);
//取得命令结果的输出流
//输出流
InputStream fis=p.getInputStream();
//用一个读输出流类去读
InputStreamReader isr=new InputStreamReader(fis);
//用缓冲器读行
BufferedReader br=new BufferedReader(isr);
String line=null;
//直到读完为止
while((line=br.readLine())!=null) {
//有可能发生阻塞的问题
System.out.println("return input Str:" + line);
}
int exitVal = p.waitFor();
System.out.println("exitVal:" + exitVal);

把上述代码中的getInputStream换做getErrorStream,也会夯住进程,因为同样只处理了两者中一者,即标准错误。

那么能不能同步处理两个流信息呢?代码如下:

try {
//执行命令
p = Runtime.getRuntime().exec(cmd);
//取得命令结果的输出流
//输出流
InputStream fis=p.getInputStream();
//错误流
InputStream ferrs=p.getErrorStream();
//用一个读输出流类去读
InputStreamReader isr=new InputStreamReader(fis);
InputStreamReader errsr=new InputStreamReader(ferrs);
//用缓冲器读行
BufferedReader br=new BufferedReader(isr);
BufferedReader errbr=new BufferedReader(errsr);
String line=null;
String lineerr = null;
//直到读完为止
while((line=br.readLine())!=null) {
//有可能发生阻塞的问题
System.out.println("return input Str:" + line);
}
while((lineerr=errbr.readLine())!=null){
//有可能发生阻塞的问题
System.out.println("return err Str:" + lineerr);
}
int exitVal = p.waitFor();
System.out.println("exitVal:" + exitVal);
} catch (Exception e) {
e.printStackTrace();
}
}

测试过后发现也不行,因为是同步的,就会有先后顺序,也会发生阻塞,测试方法,将test.sh改为只打印标准错误,就会发现标准错误处理被阻塞,脚本如下:

#!/bin/bash

for((i=0; i < 100000; i++));do
//输出到标准错误
echo "test
ac23
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1>&2
done

解决办法思路:

(1)并发处理两个流信息,开启两个线程分别处理输出流与错误流
(2)将两个流合并为一个流解决示例:
第一种思路:
class ProcessExecutor
{
private Process p;
private List<String> outputList;
private List<String> errorOutputList;
public ProcessExecutor(Process p) throws IOException
{
if(null == p)
{
throw new IOException("the provided Process is null");
}
this. p = p;
}
public List<String> getOutputList()
{
return this. outputList;
}
public List<String> getErrorOutputList()
{
return this.errorOutputList;
}
public int execute()
{
int rs = 0;
Thread outputThread = new ProcessOutputThread(this.p.getInputStream());
Thread errorOutputThread = new ProcessOutputThread(this.p.getErrorStream());
outputThread.start();
errorOutputThread.start();
rs = p.waitFor();
outputThread.join();
errorOutputThread.join();
this.outputList = outputThread.getOutputList();
this.errorOutputList = errorOutputThread.getOutputList();
return rs;
}
}

class ProcessOutputThread extends Thread
{
private InputStream is;
private List<String> outputList;
public ProcessOutputThread(InputStream is) throws IOException
{
if(null == is)
{
throw new IOException("the provided InputStream is null");
}
this. is = is;
this.outputList = new ArrayList<String>();
}
public List<String> getOutputList()
{
return this. outputList;
}
@Override
public void run()
{
InputStreamReader ir = null;
BufferedReader br = null;
try
{
ir = new InputStreamReader(this.is);
br = new BufferedReader(ir);
String output = null;
while(null != (output = br.readLine()))
{
print(output);
this.outputList.add(output);
}
}
catch(IOException e)
{
e.print();
}
finally
(
try
{
if(null != br)
{
br.close();
}
if(null != ir)
{
ir.close();
}
if(null != this.is)
{
this.is.close();
}
}
catch(IOException e)
{
e.print();
}
)
}
}

第二种思路:使用ProcessBuilder,将其redirectErrorStream(true);将输出流与错误流合并
public int execute()
{
int rs = 0;
String[] cmds = {...};//command and arg
ProcessBuilder builder = new ProcessBuilder(cmds);
builder.redirectErrorStream(true);
Process process = builder.start();
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String output = null;
while (null != (readLine = br.readLine()))
{
print(output);
}
rs = process.waitFor();
return rs;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java Process