1
votes

I am new to working with ExecutorService, Future, and Runnable in java to set up timeouts on threads. I am working on a program where my main thread will call another thread to parse an XML file and (for security purposes) time out after a certain amount of time. I have been googling for hours and read many StackOverFlow threads and I just cannot seem to get the main thread to interrupt the secondary thread at all. When I run this program, the xml parser will go on forever parsing ridiculously large files, and I cannot seem to get it to be interrupted. Any help would be greatly appreciated. My code for both threads is below.

public class xmlParser{
        private static class Parse implements Runnable {

                private final String xmlFile;

                public Parse(String xmlFile) {
                        this.xmlFile = xmlFile;
                }

                @Override
                public void run() {
                        try {
                                while (!Thread.interrupted()) {
                                        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
                                        xmlReader.setContentHandler(new MyContentHandler());
                                        xmlReader.parse(new InputSource(xmlFile));
                        }
                }

                        catch (Exception e) {
                                System.err.println("TIMEOUT ERROR: Took too long to parse xml file.");
                                e.printStackTrace();

                        }
                }

        }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future future = executor.submit(new Parse(args[0]));
        try {
                future.get(1, TimeUnit.SECONDS);
        }
        catch (Exception e) {
                future.cancel(true);
        }
        finally {
                executor.shutdownNow();
        }

   }
}

Note: I am aware of the multiple types of exceptions that future.get(long timeout, TimeUnit unit) will throw and will handle that later. Currently, I simply want my main thread to interrupt the Parse thread after 1 second of running.

1

1 Answers

0
votes

I tried to reproduce with a simpler job:

static class FiveSecJob implements Callable<String> {
    @Override
    public  String call() {
        long t0 = System.currentTimeMillis();
        try {
            Thread.sleep(5000);
            return "success";
        } catch (Exception e) {
            System.out.println("interrupted after " + (System.currentTimeMillis() - t0) / 1000d + "s: " + e);
            return e.getMessage();
        }
    }
}
@Test
public void testTimeout() {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<String> future = executor.submit(new FiveSecJob());
    String s = "initial value";
    try {
        s = future.get(1, TimeUnit.SECONDS);
    } catch (Exception e) {
        System.out.println("cancelling future (" + e + ")");
        future.cancel(true);
    } finally {
        executor.shutdownNow();
    }
    System.out.println("s: " + s);
}

It seems to cancel the job like intendend. The output is:

cancelling future (java.util.concurrent.TimeoutException)
s: initial value
interrupted after 1.0s: java.lang.InterruptedException: sleep interrupted