Process.getInputStream()차단 문제 해결

Process.getInputStream()차단 문제
자바

Runtime.getInstance().exec (String cmd)
혹은

new ProcessBuilder(String cmd).start()
하위 프로 세 스 대상 Process 를 만 들 수 있 습 니 다.process 대상 을 호출 하 는 waitFor()방법 을 통 해 메 인 프로 세 스 를 대기 상태 로 들 어가 하위 프로 세 스 가 실 행 될 때 까지 다음 작업 을 진행 할 수 있 습 니 다.하위 프로 세 스 를 잘못 처리 하면 메 인 프로 세 스 가 막 혀 프로그램 전체 가 죽 을 수 있 습 니 다.
자바 Api 에서 Process 에 대해 말 하 는 것 은:
ProcessBuilder.start()와 Runtime.exec 방법 으로 이 컴퓨터 프로 세 스 를 만 들 고 Process 하위 클래스 의 인 스 턴 스 를 되 돌려 줍 니 다.이 인 스 턴 스 는 프로 세 스 를 제어 하고 관련 정 보 를 얻 을 수 있 습 니 다.프로 세 스 클래스 는 프로 세 스 입력,출력 실행,프로 세 스 완료 대기,프로 세 스 종료 상태 검사,프로 세 스 를 없 애 는 방법 을 제공 합 니 다.
프로 세 스 를 만 드 는 방법 은 이 컴퓨터 플랫폼 의 특정 프로 세 스 를 잘 작 동 하지 못 할 수도 있 습 니 다.예 를 들 어 이 컴퓨터 창 프로 세 스,데 몬,Microsoft Windows 의 Win 16/DOS 프로 세 스,셸 스 크 립 트 등 입 니 다.만 든 하위 프로 세 스 는 터미널 이나 콘 솔 이 없습니다.모든 표준 io(즉 stdin,stdout,stderr)작업 은 세 개의 흐름(getOutputStream(),getInputStream(),getErrorStream()을 통 해 부모 프로 세 스 로 재 설정 합 니 다.부모 프로 세 스 는 이 흐름 을 사용 하여 하위 프로 세 스 의 입력 과 하위 프로 세 스에 서 출력 을 가 져 옵 니 다.일부 이 플랫폼 은 표준 입력 과 출력 흐름 에 만 제 한 된 버퍼 크기 를 제공 하기 때문에 읽 기 쓰기 서브 프로 세 스 의 출력 흐름 이나 입력 흐름 이 신속하게 실패 하면 서브 프로 세 스 가 막 히 고 심지어 잠 금 이 생 길 수 있 습 니 다.
getOutputStream(),getInputStream(),getErrorStream()에 대한 설명 에서 주의사항 이 있 습 니 다.출력 흐름 과 오류 흐름 을 버퍼 링 하 는 것 은 좋 은 생각 입 니 다!응,추상 적 이 야!
문 제 는 바로 여기에 있 습 니 다.Process.getInputStream()과 Process.getErrorStream()은 각각 Process 의 표준 출력 흐름 과 오류 흐름 을 되 돌려 줍 니 다.두 흐름 이 잘못 처리 되면 버퍼 가 제때에 제거 되 지 않 고 가득 차 면 프로 세 스 가 막 힙 니 다.Process.destory()를 호출 하 더 라 도 막 힌 하위 프로 세 스 를 없 앨 수 있 는 것 은 아 닙 니 다.
Process 의 출력 흐름 과 오류 흐름 을 동기 화하 여 처리 하려 고 시도 하면 반드시 효과 가 있 는 것 은 아 닙 니 다.순서 실행 과정 에서 출력 흐름 과 오류 흐름 은 제때에 처리 되 지 못 합 니 다.해결 방안 은 두 가지 가 있다.
프로젝트 1:process 의 출력 흐름 과 오류 흐름 을 동시에 가 져 옵 니 다.
두 스 레 드 를 시작 하여 출력 흐름 과 오류 흐름 을 동시에 읽 고 처리 합 니 다.IDE 를 열기 가 귀 찮 습 니 다.코드 를 대충 두 드 려 보 세 요.오류 가 있 을 수 있 습 니 다.다음 과 같 습 니 다.
호출 자:

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();
   }
  )
 }
}
프로젝트 2:ProcessBuilder 의 redirect ErrorStream()방법 으로 출력 흐름 과 오류 흐름 을 통합 합 니 다.

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;
} 
자바 프로 세 스 블록 테스트 총화
Process 차단 원인:입력 흐름 과 오류 흐름 이 분리 되 고 처리 하지 않 으 면 차단 이 발생 합 니 다.결국은 본질 적 으로 bio 가 야기 하 는 io 차단 문제 입 니 다.
getInputStream,getErroSteam 은 스 크 립 트 나 명령 을 가 져 오 는 콘 솔 리 턴 정보 입 니 다.전 자 는 표준 출력 리 턴 정 보 를 얻 었 고 후 자 는 표준 오류 리 턴 정 보 를 얻 었 습 니 다.
Process 원리:Runtime.getRuntime().exec(cmd)를 사용 하면 현재 프로 세 스에 하위 프로 세 스 를 만 듭 니 다.하위 프로 세 스 는 콘 솔 이 없 기 때문에 표준 출력 과 표준 오류 가 부모 프로 세 스에 되 돌아 오기 때문에 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
테스트 를 통 해 자바 Exebat.자바 파일 에서 표준 출력 이나 표준 오류 만 열 면 프로 세 스 가 다 져 져 서 waiteFor 를 통 해 반환 값 을 얻 을 수 없습니다.스 크 립 트 에서 각각 100000 w 개의 정 보 를 표준 출력 과 표준 오류 로 출력 했 기 때 문 입 니 다.다음 코드 는 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 "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 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 를 사용 하여 ErrorStream(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;  
    }  
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기