2014-10-17 61 views
1

我試圖運行一個異步過程並獲取它的輸入流(如果有的話)。Java使用Futuretask輸入流閱讀器的異步流程亞軍

這是我的代碼:

CommandCall commandCall = new CommandCall(commands); 
    ExecutorService executor = Executors.newSingleThreadExecutor(); 
    Future<Integer> task = executor.submit(commandCall); 

,這是運行任務

public class CommandCall implements Callable<Integer> { 

    private byte[] output; 
    private int retval=-1; 
    private String[] commands=null; 
    Process process=null; 

    public CommandCall(String[] commands) throws Exception { 
     this.commands=commands; 
     this.output=null; 
    } 

    public void destroy() { 
     try { 
      if(process!=null) process.destroy(); 
     }catch(Exception e){} 
    } 

    public byte[] getByteArray() { 
     return output; 
    } 


    public int getRetval() { 
     return retval; 
    } 

    @Override 
    public Integer call() throws Exception { 
     try{ 
      process = new ProcessBuilder(commands).start(); 
      // here i must read the process input and store it to this.output 
      // this must be a non lockable read because the process can be without any output 
      retval= process.waitFor(); 
     }finally{ 
      try{ 
       if(bos!=null) bos.close(); 
      }catch(Exception e){} 
     } 
     return retval; 
    } 

} 

我不能讓輸出的過程,請介意的2個非常重要的過程:

  • 過程必須是異步的,因爲我需要管理超時
  • Pro cess的InputStream可以是可選的,並且不能鎖定等待內容的線程:可以有一個沒有任何輸出的進程。

UPDATE

我想這個版本...似乎工作,但我不知道它是強大到足夠多。

@Override 
public Integer call() throws Exception { 
    InputStream is=null; 
    try{ 
     process = new ProcessBuilder(commands).start(); 
     is=process.getInputStream(); 
     int len; 
     int size = 1024; 
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     byte[] buf = new byte[size]; 
     while ((len = is.read(buf, 0, size)) != -1) 
      bos.write(buf, 0, len); 
     output = bos.toByteArray(); 
     retval= process.waitFor(); 
    }finally{ 
     try{ 
      if(is!=null) is.close(); 
     }catch(Exception e){} 
    } 
    return retval; 
} 

回答

0

觀察進程輸出的行爲本身應該在自己的線程中。一旦進程終止,該線程也應該終止。

所以基本上你可以這樣做:

@Override 
public Integer call() throws Exception { 
    Thread outputObserver = null; 

    try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 
     this.process = new ProcessBuilder(commands).start(); 
     outputObserver = new OutputObserver(process.getInputStream(), baos); 
     outputObserver.start(); 
     this.retval = process.waitFor(); 
     this.output = baos.toByteArray(); 
    }finally{ 
     if(outputObserver != null) outputObserver.interrupt(); 
    } 

    return this.retval; 
} 

private static OutputObserver extends Thread { 
    private final InputStream input; 
    private final OutputStream output; 

    OutputObserver(InputStream input, OutputStream output) { 
     this.input = input; 
     this.output = output; 
    } 

    @Override 
    public void run() { 
     while(!Thread.interrupted()) { 
      // Copy bytes from input to output here (handling any exceptions). 
      // Maybe use 3rd party libs for that. 
     } 
     // Make sure to copy remaining bytes here, too. 
    } 
} 

有兩點要注意:

  • 我沒有測試任何代碼。也許我犯了一些微妙的錯誤,但方向應該清楚。
  • 通常我不會延伸Thread,而是實施Runnable。我只是想不要讓事情過分複雜化。
  • 我沒有提供將輸入流中的字節複製到輸出流的代碼。如果您需要幫助,請在網上進行一些搜索。你會發現很多解決方案。