views:

112

answers:

3

Am working on a program in Java as shown below, the class is simply executing commands into the operating system level. Yet, sometimes we are facing a problem where the program get stuck, so the command never return any status, thus the thread does not terminate.

Now am trying to enhance the code, add an additional thing where I can kill specific threads. Am already capturing the ThreadId, so, is this doable?

public class ExecuteCmd implements Runnable {

String ProcessId;
String cmd;
BackendSQL bsql;
Logger myLogger;
Thread myThread;


public ExecuteCmd(String cmd, BackendSQL bsql, String ProcessId, Logger myLogger) { 
   this.ProcessId=ProcessId; 
   this.cmd=cmd; 
   this.bsql=bsql;
   this.myLogger=myLogger;
}

public void run() {
    int rc = 0;

    try {

        long ThreadId = Thread.currentThread().getId();


        bsql.MarkRunning(ProcessId, ThreadId);

        myLogger.debug("[ExecuteCmd] Command is: "+cmd);

        String[] cmdFull = cmd.split(";");

        Runtime rt = Runtime.getRuntime();

        Process p = rt.exec(cmdFull);
        myLogger.info("[ExecuteCmd] [Threading] "+ ThreadId + ": Executing command");
        myLogger.debug("[ExecuteCmd] Command is: "+cmd);


        BufferedReader inStream = new BufferedReader(new InputStreamReader(p.getInputStream()));

        String inStreamLine = null;
        String inStreamLinebyLine=null;
        while((inStreamLine = inStream.readLine()) != null) {
          inStreamLinebyLine = inStreamLinebyLine+"\n"+inStreamLine;
        }
        myLogger.info("Command getInputStream: " + inStreamLinebyLine);



        try {
            rc = p.waitFor();

            if (rc == 0) {
                bsql.MarkCompleted(ProcessId);
            }else{
                bsql.MarkFailed(ProcessId);
            }

        } catch (InterruptedException intexc) {
            System.out.println("Interrupted Exception on waitFor: " +
                               intexc.getMessage());
        }

    }catch (IOException IOE) {
        myLogger.error("IOException[ExecuteCmd]: " + IOE.getMessage());
    }catch (Exception e) {
        myLogger.error("Exception[ExecuteCmd]: " + e.getMessage());
    }
}

}

A: 

I think Thread.Interrupt is what you'r looking for, you'll have to call it from another thread though.

http://java.sun.com/javase/6/docs/api/java/lang/Thread.html#interrupt%28%29

dutt
+1  A: 

To address your fundamental problem (and thus why you need to interrupt a process), I suspect your process is hanging because you're not consuming standard out and standard error concurrently. Basically, if you don't do this, then your spawned process can hang. It's a very common problem. See this answer for more information and a fix.

Also see this Java Specialist newsletter on how to shutdown threads cleanly (via Thread.interrupt()), and how to handle this properly in the thread being shut down. You'll need to handle a shutdown properly even if you use Executor frameworks and the like, since they'll interrupt your thread.

I don't believe you'll need this, however, if you consume your program stdout/err concurrently.

Brian Agnew
Brian Not only is that page over-complicated and somewhat incorrect (he should be checking Thread.isInterrupted(), not sleeping and he should not be extending Thread), its over 7 years out of date. The java.util.concurrent has obviated the need to directly interact with Thread classes.
Kevin
It may be 7 years old, but it's still perfectly valid. As to whether it's better to be using the java.util.concurrent package, that's another question. The fundamental issue of how to stop a thread, and how to handle it correctly remains.
Brian Agnew
And in fact you'll see that he's using Thread.isInterrupted() (last example, if memory serves)
Brian Agnew
+3  A: 

You should submit instances of ExecuteCmd to an ExecutorService. That way, you can cancel or interrupt your tasks.

Process#waitFor() is an interruptible operation so it should work OK.

ExecutorService service = Executors.newSingleThreadedExecutor();
Future<?> future = service.submit(new ExecuteCmd(...));
if (takingTooLong()) {
    future.cancel(true);
}
Kevin