Open Stream和CancellationException导致Thread锁定

时间:2013-07-15 21:28:07

标签: java android multithreading exception memory-leaks

我有一个相当复杂的应用程序,我正在尝试为Android手机创建。我有一个使用Java Process Builder的类和一些私有类来读取输入和输出流。

当我尝试ping的IP由于进程卡住而没有响应线程锁时,执行程序服务在2分钟后决定关闭。这样可以避免整个应用程序锁定,但两个流永远不会关闭,并且流的线程保持打开状态。

知道如何杀死流线程吗?

class StreamGobbler extends Thread {
    InputStream is;
    String type;

    StreamGobbler(InputStream is, String type) {
        this.is = is;
        this.type = type;
    }

    public void run() {
        InputStreamReader isr = new InputStreamReader(is);
        try {

            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null){
                System.out.println(type + ">" + line); 
            }
        } catch (IOException e) {
            LogWriter.getInstance().writeToLogFile(e);
        }finally{

            try {
                if(is != null){
                    is.close();
                }
                if(isr != null){
                    isr.close();
                }
            } catch (IOException e) {
                LogWriter.getInstance().writeToLogFile(e);
            }
        }
    }

    public void kill() {

        Thread.currentThread().interrupt();
    }
}



public class PingRunner implements Callable<Double>{

private String pingVal;
private int exitVal;
private double laten;
private String ipAddress;

public PingRunner(String ipAddress) {
    pingVal = "";
    exitVal = -1;
    laten = -1;
    this.ipAddress = ipAddress;
}

@Override
public Double call() throws Exception {

    List<String> commands = new ArrayList<String>();
    commands.add("ping");
    commands.add("-c");
    commands.add("5");
    commands.add(ipAddress);
    try {
        this.doCommand(commands);
    } catch (IOException e) {
        LogWriter.getInstance().writeToLogFile(e);
    }

    return laten;
}

    private void doCommand(List<String> command) throws IOException{

    ProcessBuilder pb = new ProcessBuilder(command);
    Process process = pb.start();

    // any error message?
    StreamGobbler errorGobbler = new StreamGobbler(
            process.getErrorStream(), "ERROR");

    // any output?
    OutputStreamGobbler outputGobbler = new OutputStreamGobbler(
            process.getInputStream(), "OUTPUT");

    // kick them off
    errorGobbler.start();
    outputGobbler.start();

    // read the output from the command
    try {
        exitVal = process.waitFor();
        //Sleep for 10 secs to try to clear the buffer
        Thread.sleep(10000);

        //pingVal = echo.toString();
        if(exitVal == 0 && !pingVal.isEmpty()){
            //System.out.println("PING STATS: "+pingVal);
            try{
            pingVal = pingVal.substring(pingVal.lastIndexOf("rtt min/avg/max/mdev"));
            pingVal = pingVal.substring(23);
            pingVal = pingVal.substring(pingVal.indexOf("/")+1);
            laten = Double.parseDouble(pingVal.substring(0,pingVal.indexOf("/")));
            }catch (IndexOutOfBoundsException e){
                System.out.println("PING VAL: "+ pingVal);
                LogWriter.getInstance().writeToLogFile(e);
            }
        }


    } catch (InterruptedException e) {
        LogWriter.getInstance().writeToLogFile(e);
        errorGobbler.kill();
        outputGobbler.kill();
    }finally{
        errorGobbler = null;
        outputGobbler = null;
    }
    System.out.println("ExitValue: " + exitVal);
}

在我的主要课程中,我有这种方法:

protected void ping() {
    laten = -1;
    serverIP = serverIPs.get(testIndex % 3);

    PingRunner pRunner = new PingRunner(serverIP);

    Set<Callable<Double>> runner = new HashSet<Callable<Double>>();
    runner.add(pRunner);

    ExecutorService executor = Executors.newSingleThreadExecutor();

    try {
        laten = executor.submit(pRunner).get(2, TimeUnit.MINUTES);
        executor.shutdown();

    } catch (InterruptedException e) {
        LogWriter.getInstance().writeToLogFile(e);
    } catch (ExecutionException e) {
        LogWriter.getInstance().writeToLogFile(e);
    } catch (CancellationException e) {
        pRunner.kill();
        executor.shutdown();
        LogWriter.getInstance().writeToLogFile(e);
        LogWriter.getInstance().writeToLogFile(
                "ERROR: Unable to ping server: " + serverIP);
    } catch (TimeoutException e) {
        pRunner.kill();
        executor.shutdown();
        LogWriter.getInstance().writeToLogFile(e);
        LogWriter.getInstance().writeToLogFile(
                "ERROR: Unable to ping server: " + serverIP);
    } finally {
        executor = null;
        System.gc();
    }

1 个答案:

答案 0 :(得分:0)

  

知道如何杀死流线程吗?

不确定发生了什么,但我看到的一个错误是:

public void kill() {
    Thread.currentThread().interrupt();
}

这是打断调用者线程,而不是gobbler线程。那应该是:

public void kill() {
    // kill the gobbler thread
    interrupt();
}

这是正确的,因为StreamGobbler扩展了Thread。与往常一样,建议您implements Runnable然后根据需要设置private Thread field。然后你会做thread.interrupt();

之类的事情

此外,您没有正确关闭流。通常,当我将一个流包装在另一个流中时,我将包装的流设置为null。此外,您还没有关闭br BufferedReader。代码应该是:

InputStreamReader isr = new InputStreamReader(is);
is = null;
BufferedReader br = null;
try {
    br = new BufferedReader(isr);
    isr = null;
    String line = null;
    ...
} finally {
    // IOException catches not listed
    if(br != null){
        br.close();
    }
    if(isr != null){
        isr.close();
    }
    if(is != null){
        is.close();
    }
}

我是拥有org.apache.commons.io.IOUtils方法的closeQuietly(...)软件包的忠实粉丝,该方法将finally块转换为:

IOUtils.closeQuietly(br);
IOUtils.closeQuietly(isr);
IOUtils.closeQuietly(is);