How to timeout a thread

后端 未结 17 1116
时光说笑
时光说笑 2020-11-22 01:01

I want to run a thread for some fixed amount of time. If it is not completed within that time, I want to either kill it, throw some exception, or handle it in some way. How

相关标签:
17条回答
  • 2020-11-22 01:47

    I was looking for an ExecutorService that can interrupt all timed out Runnables executed by it, but found none. After a few hours I created one as below. This class can be modified to enhance robustness.

    public class TimedExecutorService extends ThreadPoolExecutor {
        long timeout;
        public TimedExecutorService(int numThreads, long timeout, TimeUnit unit) {
            super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(numThreads + 1));
            this.timeout = unit.toMillis(timeout);
        }
    
        @Override
        protected void beforeExecute(Thread thread, Runnable runnable) {
            Thread interruptionThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // Wait until timeout and interrupt this thread
                        Thread.sleep(timeout);
                        System.out.println("The runnable times out.");
                        thread.interrupt();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            interruptionThread.start();
        }
    }
    

    Usage:

    public static void main(String[] args) {
    
        Runnable abcdRunnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("abcdRunnable started");
                try {
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    // logger.info("The runnable times out.");
                }
                System.out.println("abcdRunnable ended");
            }
        };
    
        Runnable xyzwRunnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("xyzwRunnable started");
                try {
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    // logger.info("The runnable times out.");
                }
                System.out.println("xyzwRunnable ended");
            }
        };
    
        int numThreads = 2, timeout = 5;
        ExecutorService timedExecutor = new TimedExecutorService(numThreads, timeout, TimeUnit.SECONDS);
        timedExecutor.execute(abcdRunnable);
        timedExecutor.execute(xyzwRunnable);
        timedExecutor.shutdown();
    }
    
    0 讨论(0)
  • 2020-11-22 01:48

    BalusC said:

    Update: to clarify a conceptual misunderstanding, the sleep() is not required. It is just used for SSCCE/demonstration purposes. Just do your long running task right there in place of sleep().

    But if you replace Thread.sleep(4000); with for (int i = 0; i < 5E8; i++) {} then it doesn't compile, because the empty loop doesn't throw an InterruptedException.

    And for the thread to be interruptible, it needs to throw an InterruptedException.

    This seems like a serious problem to me. I can't see how to adapt this answer to work with a general long-running task.

    Edited to add: I reasked this as a new question: [ interrupting a thread after fixed time, does it have to throw InterruptedException? ]

    0 讨论(0)
  • 2020-11-22 01:49

    I think the answer mainly depends on the task itself.

    • Is it doing one task over and over again?
    • Is it necessary that the timeout interrupts a currently running task immediately after it expires?

    If the first answer is yes and the second is no, you could keep it as simple as this:

    public class Main {
    
        private static final class TimeoutTask extends Thread {
            private final long _timeoutMs;
            private Runnable _runnable;
    
            private TimeoutTask(long timeoutMs, Runnable runnable) {
                _timeoutMs = timeoutMs;
                _runnable = runnable;
            }
    
            @Override
            public void run() {
                long start = System.currentTimeMillis();
                while (System.currentTimeMillis() < (start + _timeoutMs)) {
                    _runnable.run();
                }
                System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms");
            }
    
        }
    
        public static void main(String[] args) throws Exception {
            new TimeoutTask(2000L, new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("doing something ...");
                    try {
                        // pretend it's taking somewhat longer than it really does
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    }
    

    If this isn't an option, please narrow your requirements - or show some code.

    0 讨论(0)
  • 2020-11-22 01:50

    Great answer by BalusC's:

    but Just to add that the timeout itself does not interrupt the thread itself. even if you are checking with while(!Thread.interrupted()) in your task. if you want to make sure thread is stopped you should also make sure future.cancel() is invoked when timeout exception is catch.

    package com.stackoverflow.q2275443; 
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    
    
    public class Test { 
        public static void main(String[] args) throws Exception {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<String> future = executor.submit(new Task());
    
            try { 
                System.out.println("Started..");
                System.out.println(future.get(3, TimeUnit.SECONDS));
                System.out.println("Finished!");
            } catch (TimeoutException e) {
                //Without the below cancel the thread will continue to live 
                // even though the timeout exception thrown.
                future.cancel();
                System.out.println("Terminated!");
            } 
    
            executor.shutdownNow();
        } 
    } 
    
    class Task implements Callable<String> {
        @Override 
        public String call() throws Exception {
          while(!Thread.currentThread.isInterrupted()){
              System.out.println("Im still running baby!!");
          }          
        } 
    } 
    
    0 讨论(0)
  • 2020-11-22 01:51

    One thing that I've not seen mentioned is that killing threads is generally a Bad Idea. There are techniques for making threaded methods cleanly abortable, but that's different to just killing a thread after a timeout.

    The risk with what you're suggesting is that you probably don't know what state the thread will be in when you kill it - so you risk introducing instability. A better solution is to make sure your threaded code either doesn't hang itself, or will respond nicely to an abort request.

    0 讨论(0)
提交回复
热议问题