如何使线程超时


255

我想在一个固定的时间内运行一个线程。如果未在该时间内完成,我想杀死它,抛出一些异常或以某种方式处理它。如何做呢?

我从该线程 了解到的一种方法是在该线程的run()方法内使用TimerTask。

有没有更好的解决方案呢?

 
编辑:添加赏金,因为我需要一个更明确的答案。下面给出的ExecutorService代码无法解决我的问题。为什么在执行完某些代码后就进入sleep()-我无法处理这段代码?如果代码已完成并且sleep()被中断,那怎么可能是超时呢?

需要执行的任务不在我的控制范围内。它可以是任何代码。问题是这段代码可能会陷入无限循环。我不希望那样发生。因此,我只想在单独的线程中运行该任务。父线程必须等到该线程完成并且需要知道任务的状态(即它是否超时还是发生某些异常,或者它是否成功)。如果任务进入无限循环,则我的父线程将无限期地等待,这不是理想的情况。


编辑:添加赏金,因为我需要更明确的答案。下面给出的ExecutorService代码无法解决我的问题。为什么执行代码后我应该sleep()?如果代码已完成并且sleep()被中断,那怎么可能是超时呢?
java_geek

7
sleep()只是代表“长时间运行任务”的存根。只需将其替换为您的实际任务即可;)
BalusC 2010年

1
...一个“长时间运行的任务”恰好响应interrupt()其线程上的调用...并非所有“阻塞”调用都可以,正如我试图在答案中指出的那样。您要中止的任务的详细信息在应该使用的方法上有很大的不同。有关该任务的更多信息将很有帮助。
erickson 2010年

如果这些答案不能解决问题,那么我想更多的详细信息/代码应有助于回答。
Elister

您要限制时间的这些线程;他们是在进行阻塞调用,还是处于某个循环中,您可以轻松地检查某个变量以查看是否该退出了?
Scott Smith'3

Answers:


376

实际上,使用SSCCEExecutorService代替,而不是Timer它是

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) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

用方法中的timeout参数播放一点Future#get(),例如将其增加到5,您将看到线程完成。您可以在catch (TimeoutException e)块中拦截超时。

更新:澄清一个概念的误解,将sleep()必需的。它仅用于SSCCE /演示目的。只要做你的长时间运行的任务就在那里发生的sleep()。在长时间运行的任务中,应检查线程是否未中断,如下所示:

while (!Thread.interrupted()) {
    // Do your long running task here.
}

24
Thread.sleep(4000)其他长时间运行的语句替换,该示例将无法正常工作。换句话说,该示例Task旨在了解Thread.isInterrupted()状态更改的情况下才有效。
yegor256

@BalusC我尝试了这种方法来尝试终止线程,但是无法使其正常工作。您可以在此处查看:stackoverflow.com/questions/35553420/…–
syfantid

future.cancel(true)如何处理InterruptedException?
bolei 2016年

1
n个人对软件包名称发表了评论,这里还有+1。这是一个很好的技能,可以被吸收。谢谢!
Ashwin Tumma '16

@BalusC我怀疑,Future是否会同步执行,并且如果花费的时间超过了预定义的时间,那么它将终止。否则它将在将来的某个时间执行,同时我们正在按时计数...谢谢
Adeel Ahmad

49

对于任何旧任务,没有100%可靠的方法来执行此操作。在编写任务时必须牢记这一能力。

核心Java库,例如通过对工作线程的调用来ExecutorService取消异步任务interrupt()。因此,例如,如果任务包含某种循环,则应在每次迭代时检查其中断状态。如果任务正在执行I / O操作,则它们也应该是可中断的-设置起来可能很棘手。无论如何,请记住代码必须主动检查中断;设置中断不一定会做任何事情。

当然,如果您的任务是一个简单的循环,则只需检查每次迭代的当前时间,并在经过指定的超时时就放弃。在这种情况下,不需要工作线程。


以我的经验,唯一不响应开始中断的代码是在本机代码中阻塞(等待操作系统)。
托尔比约恩Ravn的安徒生

@ThorbjørnRavnAndersen我同意,但这是很多代码。我的观点是,对此没有通用的机制。您必须了解任务的中断策略。
erickson

@埃里克森,我同意你的看法。明确的答案是,如果您有兴趣以某种方式停止它,则必须为每个任务定义一个取消策略。否则线程应该知道中断时应该做什么。毕竟,中断和停止任何线程仅仅是目标线程可能接受或拒绝的请求,因此编写任务时最好记住这一点。
AKS 2013年

executorservice无法选择在调用线程上运行任务吗?executorservice也可能会在将来某个时候选择执行任务吗?
filthy_wizard 2015年

@ user1232726 execute()父接口的方法Executor可以在调用线程中运行任务。对于返回实例的submit()方法,没有类似的声明。该服务的含义是,必须通过关闭来清理工作线程,并且任务是异步执行的。也就是说,合同中没有任何内容禁止在提交线程中执行任务。这些保证来自工厂等实现API 。ExecutorServiceFutureExecutorServiceExecutors
erickson 2015年

13

考虑使用ExecutorService的实例。这两个invokeAll()invokeAny()方法都可以用timeout参数。

当前线程将阻塞,直到该方法完成(不确定是否需要这样做),原因是该任务正常完成或已达到超时。您可以检查返回的Future(s)以确定发生了什么。


9

假设线程代码不受您的控制:

从上面提到的Java 文档中:

如果线程不响应Thread.interrupt怎么办?

在某些情况下,您可以使用特定于应用程序的技巧。例如,如果某个线程正在一个已知的套接字上等待,则可以关闭该套接字以使该线程立即返回。不幸的是,实际上没有任何一种技术可以正常工作。应该注意的是,在所有等待线程不响应Thread.interrupt的情况下,它也不响应Thread.stop。这样的情况包括故意的拒绝服务攻击,以及thread.stop和thread.interrupt无法正常工作的I / O操作。

底线:

确保所有线程都可以被中断,否则您需要对该线程有特定的了解-例如要设置一个标志。也许您可以要求将任务以及停止该任务所需的代码提供给您-使用stop()方法定义接口。您也可以在停止任务失败时发出警告。


8

BalusC说:

更新:为了澄清概念上的误解,不需要sleep()。它仅用于SSCCE /演示目的。只需在那里执行您的长期运行任务即可代替sleep()。

但是,如果替换为Thread.sleep(4000);for (int i = 0; i < 5E8; i++) {}则它不会编译,因为空循环不会抛出InterruptedException

为了使线程可中断,它需要抛出一个InterruptedException

对我来说,这似乎是一个严重的问题。我看不到如何使这个答案适合一般的长期运行任务。

编辑添加:我将其作为新问题重新提出:[ 在固定时间后中断线程,是否必须抛出InterruptedException?]


我这样做的方法是在公共Class <T>调用{}方法中添加“ throws Exception”
Roberto Linares

5

我认为您应该看一下适当的并发处理机制(运行到无限循环中的线程本身听起来并不好,顺便说一句)。确保您已阅读有关“杀死”或“停止”线程主题的内容。

您所描述的听起来很像“集合点”,因此您可能需要看看CyclicBarrier

可能还有其他结构(例如使用CountDownLatch)可以解决您的问题(一个线程等待锁存器超时,另一个线程应在其完成工作后递减锁存器,这将在之后释放您的第一个线程超时或调用闩锁倒计时)。

我通常推荐这方面的两本书:Java并发编程Java并发实践


5

前一段时间,我为此创建了一个帮助器类。效果很好:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
 * TimeOut class - used for stopping a thread that is taking too long
 * @author Peter Goransson
 *
 */
public class TimeOut {

    Thread interrupter;
    Thread target;
    long timeout;
    boolean success;
    boolean forceStop;

    CyclicBarrier barrier;

    /**
     * 
     * @param target The Runnable target to be executed
     * @param timeout The time in milliseconds before target will be interrupted or stopped
     * @param forceStop If true, will Thread.stop() this target instead of just interrupt() 
     */
    public TimeOut(Runnable target, long timeout, boolean forceStop) {      
        this.timeout = timeout;
        this.forceStop = forceStop;

        this.target = new Thread(target);       
        this.interrupter = new Thread(new Interrupter());

        barrier = new CyclicBarrier(2); // There will always be just 2 threads waiting on this barrier
    }

    public boolean execute() throws InterruptedException {  

        // Start target and interrupter
        target.start();
        interrupter.start();

        // Wait for target to finish or be interrupted by interrupter
        target.join();  

        interrupter.interrupt(); // stop the interrupter    
        try {
            barrier.await(); // Need to wait on this barrier to make sure status is set
        } catch (BrokenBarrierException e) {
            // Something horrible happened, assume we failed
            success = false;
        } 

        return success; // status is set in the Interrupter inner class
    }

    private class Interrupter implements Runnable {

        Interrupter() {}

        public void run() {
            try {
                Thread.sleep(timeout); // Wait for timeout period and then kill this target
                if (forceStop) {
                  target.stop(); // Need to use stop instead of interrupt since we're trying to kill this thread
                }
                else {
                    target.interrupt(); // Gracefully interrupt the waiting thread
                }
                System.out.println("done");             
                success = false;
            } catch (InterruptedException e) {
                success = true;
            }


            try {
                barrier.await(); // Need to wait on this barrier
            } catch (InterruptedException e) {
                // If the Child and Interrupter finish at the exact same millisecond we'll get here
                // In this weird case assume it failed
                success = false;                
            } 
            catch (BrokenBarrierException e) {
                // Something horrible happened, assume we failed
                success = false;
            }

        }

    }
}

它的名称如下:

long timeout = 10000; // number of milliseconds before timeout
TimeOut t = new TimeOut(new PhotoProcessor(filePath, params), timeout, true);
try {                       
  boolean sucess = t.execute(); // Will return false if this times out
  if (!sucess) {
    // This thread timed out
  }
  else {
    // This thread ran completely and did not timeout
  }
} catch (InterruptedException e) {}  

3

我向您发布了一段代码,它们显示了解决问题的方法。例如,我正在读取文件。您可以将此方法用于其他操作,但是您需要实现kill()方法,以便主操作将被中断。

希望能帮助到你


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Main class
 * 
 * @author el
 * 
 */
public class Main {
    /**
     * Thread which perform the task which should be timed out.
     * 
     * @author el
     * 
     */
    public static class MainThread extends Thread {
        /**
         * For example reading a file. File to read.
         */
        final private File fileToRead;
        /**
         * InputStream from the file.
         */
        final private InputStream myInputStream;
        /**
         * Thread for timeout.
         */
        final private TimeOutThread timeOutThread;

        /**
         * true if the thread has not ended.
         */
        boolean isRunning = true;

        /**
         * true if all tasks where done.
         */
        boolean everythingDone = false;

        /**
         * if every thing could not be done, an {@link Exception} may have
         * Happens.
         */
        Throwable endedWithException = null;

        /**
         * Constructor.
         * 
         * @param file
         * @throws FileNotFoundException
         */
        MainThread(File file) throws FileNotFoundException {
            setDaemon(false);
            fileToRead = file;
            // open the file stream.
            myInputStream = new FileInputStream(fileToRead);
            // Instantiate the timeout thread.
            timeOutThread = new TimeOutThread(10000, this);
        }

        /**
         * Used by the {@link TimeOutThread}.
         */
        public void kill() {
            if (isRunning) {
                isRunning = false;
                if (myInputStream != null) {
                    try {
                        // close the stream, it may be the problem.
                        myInputStream.close();
                    } catch (IOException e) {
                        // Not interesting
                        System.out.println(e.toString());
                    }
                }
                synchronized (this) {
                    notify();
                }
            }
        }

        /**
         * The task which should be timed out.
         */
        @Override
        public void run() {
            timeOutThread.start();
            int bytes = 0;
            try {
                // do something
                while (myInputStream.read() >= 0) {
                    // may block the thread.
                    myInputStream.read();
                    bytes++;
                    // simulate a slow stream.
                    synchronized (this) {
                        wait(10);
                    }
                }
                everythingDone = true;
            } catch (IOException e) {
                endedWithException = e;
            } catch (InterruptedException e) {
                endedWithException = e;
            } finally {
                timeOutThread.kill();
                System.out.println("-->read " + bytes + " bytes.");
                isRunning = false;
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    }

    /**
     * Timeout Thread. Kill the main task if necessary.
     * 
     * @author el
     * 
     */
    public static class TimeOutThread extends Thread {
        final long timeout;
        final MainThread controlledObj;

        TimeOutThread(long timeout, MainThread controlledObj) {
            setDaemon(true);
            this.timeout = timeout;
            this.controlledObj = controlledObj;
        }

        boolean isRunning = true;

        /**
         * If we done need the {@link TimeOutThread} thread, we may kill it.
         */
        public void kill() {
            isRunning = false;
            synchronized (this) {
                notify();
            }
        }

        /**
         * 
         */
        @Override
        public void run() {
            long deltaT = 0l;
            try {
                long start = System.currentTimeMillis();
                while (isRunning && deltaT < timeout) {
                    synchronized (this) {
                        wait(Math.max(100, timeout - deltaT));
                    }
                    deltaT = System.currentTimeMillis() - start;
                }
            } catch (InterruptedException e) {
                // If the thread is interrupted,
                // you may not want to kill the main thread,
                // but probably yes.
            } finally {
                isRunning = false;
            }
            controlledObj.kill();
        }
    }

    /**
     * Start the main task and wait for the end.
     * 
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        MainThread main = new MainThread(new File(args[0]));
        main.start();
        try {
            while (main.isRunning) {
                synchronized (main) {
                    main.wait(1000);
                }
            }
            long stop = System.currentTimeMillis();

            if (main.everythingDone)
                System.out.println("all done in " + (stop - start) + " ms.");
            else {
                System.out.println("could not do everything in "
                        + (stop - start) + " ms.");
                if (main.endedWithException != null)
                    main.endedWithException.printStackTrace();
            }
        } catch (InterruptedException e) {
            System.out.println("You've killed me!");
        }
    }
}

问候


3

这是我使用帮助程序类来运行调用 Java代码的非常简单的方法 :-)

这是基于优秀的答案来自BalusC

package com.mycompany.util.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
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;

/**
 * Calling {@link Callable#call()} or Running {@link Runnable#run()} code
 * with a timeout based on {@link Future#get(long, TimeUnit))}
 * @author pascaldalfarra
 *
 */
public class CallableHelper
{

    private CallableHelper()
    {
    }

    public static final void run(final Runnable runnable, int timeoutInSeconds)
    {
        run(runnable, null, timeoutInSeconds);
    }

    public static final void run(final Runnable runnable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        call(new Callable<Void>()
        {
            @Override
            public Void call() throws Exception
            {
                runnable.run();
                return null;
            }
        }, timeoutCallback, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, int timeoutInSeconds)
    {
        return call(callable, null, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try
        {
            Future<T> future = executor.submit(callable);
            T result = future.get(timeoutInSeconds, TimeUnit.SECONDS);
            System.out.println("CallableHelper - Finished!");
            return result;
        }
        catch (TimeoutException e)
        {
            System.out.println("CallableHelper - TimeoutException!");
            if(timeoutCallback != null)
            {
                timeoutCallback.run();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            executor.shutdownNow();
            executor = null;
        }

        return null;
    }

}

2

以下代码段将在一个单独的线程中启动一个操作,然后等待最多10秒钟以完成该操作。如果操作未及时完成,则代码将尝试取消该操作,然后继续进行。即使无法轻松取消操作,父线程也不会等待子线程终止。

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    public SomeClass call() {
        // Perform long-running task, return result. The code should check
        // interrupt status regularly, to facilitate cancellation.
    }
});
try {
    // Real life code should define the timeout as a constant or
    // retrieve it from configuration
    SomeClass result = future.get(10, TimeUnit.SECONDS);
    // Do something with the result
} catch (TimeoutException e) {
    future.cancel(true);
    // Perform other error handling, e.g. logging, throwing an exception
}

getExecutorService()方法可以以多种方式来实现。如果您没有任何特殊要求,则可以简单地调用Executors.newCachedThreadPool()线程池,而线程数量没有上限。


需要进口什么?什么是SomeClassFuture
ADTC

2

我没有看到的一件事是杀死线程通常是一个坏主意。有一些使线程方法可以完全中止的技术,但这与仅在超时后杀死线程不同。

您所建议的风险是,您可能不知道在杀死线程时线程将处于何种状态-因此您可能会引入不稳定性。更好的解决方案是确保线程代码不会自行挂起,或者会很好地响应中止请求。


在没有上下文的情况下,像您这样的陈述听起来过于严格。在学术环境中,我经常需要测试某些东西直到超时,当它发生时,我只需丢弃所有计算并记录发生超时的时间。它在行业中可能很少见,但仍然...
Alessandro S.

@AlessandroS:这是一个合理的观点,尽管OP要求“更好的解决方案”,我的意思是说,相比于蛮力,健壮性和可靠性更受青睐。
Dan Puzey

2

BalusC的好答案:

但只是要补充一点,超时本身不会中断线程本身。即使您在任务中使用while(!Thread.interrupted())进行检查。如果要确保线程已停止,还应确保在捕获超时异常时调用future.cancel()。

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

我认为答案主要取决于任务本身。

  • 它一次又一次地执行一项任务吗?
  • 超时到期后是否有必要立即中断当前正在运行的任务?

如果第一个答案为是,第二个答案为否,则可以像下面这样简单:

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();
    }
}

如果这不是一个选择,请缩小您的要求-或显示一些代码。


0

我正在寻找一个ExecutorService,它可以中断它执行的所有超时Runnable,但没有找到。几个小时后,我创建了一个如下。可以修改此类以增强鲁棒性。

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();
    }
}

用法:

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

现在,我遇到这样的问题。碰巧对图片进行解码。解码过程花费太多时间,导致屏幕保持黑屏。l添加一个时间控制器:当时间太长时,则从当前线程弹出。以下是差异:

   ExecutorService executor = Executors.newSingleThreadExecutor();
   Future<Bitmap> future = executor.submit(new Callable<Bitmap>() {
       @Override
       public Bitmap call() throws Exception {
       Bitmap bitmap = decodeAndScaleBitmapFromStream(context, inputUri);// do some time consuming operation
       return null;
            }
       });
       try {
           Bitmap result = future.get(1, TimeUnit.SECONDS);
       } catch (TimeoutException e){
           future.cancel(true);
       }
       executor.shutdown();
       return (bitmap!= null);

0

我有同样的问题。所以我想出了一个像这样的简单解决方案。

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

保证if块在规定时间内没有执行。该过程将终止并引发异常。

例如:

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code 
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }

0

BalusC提供的解决方案中,主线程将在超时期间保持阻塞状态。如果您的线程池中有多个线程,则将需要相同数量的其他线程,这些线程将使用Future.get(long timeout,TimeUnit unit)阻塞调用来等待并在超过超时时间时关闭线程。

解决此问题的通用方法是创建一个可以添加超时功能的ThreadPoolExecutor装饰器。这个Decorator类应该创建与ThreadPoolExecutor一样多的线程,并且所有这些线程仅应用于等待和关闭ThreadPoolExecutor。

通用类应按以下方式实现:

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolDecorator extends ThreadPoolExecutor {


    private final ThreadPoolExecutor commandThreadpool;
    private final long timeout;
    private final TimeUnit unit;

    public TimeoutThreadPoolDecorator(ThreadPoolExecutor threadpool,
                                      long timeout,
                                      TimeUnit unit ){
        super(  threadpool.getCorePoolSize(),
                threadpool.getMaximumPoolSize(),
                threadpool.getKeepAliveTime(TimeUnit.MILLISECONDS),
                TimeUnit.MILLISECONDS,
                threadpool.getQueue());

        this.commandThreadpool = threadpool;
        this.timeout=timeout;
        this.unit=unit;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(() -> {
            Future<?> future = commandThreadpool.submit(command);
            try {
                future.get(timeout, unit);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException | TimeoutException e) {
                throw new RejectedExecutionException(e);
            } finally {
                future.cancel(true);
            }
        });
    }

    @Override
    public void setCorePoolSize(int corePoolSize) {
        super.setCorePoolSize(corePoolSize);
        commandThreadpool.setCorePoolSize(corePoolSize);
    }

    @Override
    public void setThreadFactory(ThreadFactory threadFactory) {
        super.setThreadFactory(threadFactory);
        commandThreadpool.setThreadFactory(threadFactory);
    }

    @Override
    public void setMaximumPoolSize(int maximumPoolSize) {
        super.setMaximumPoolSize(maximumPoolSize);
        commandThreadpool.setMaximumPoolSize(maximumPoolSize);
    }

    @Override
    public void setKeepAliveTime(long time, TimeUnit unit) {
        super.setKeepAliveTime(time, unit);
        commandThreadpool.setKeepAliveTime(time, unit);
    }

    @Override
    public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
        super.setRejectedExecutionHandler(handler);
        commandThreadpool.setRejectedExecutionHandler(handler);
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> taskList = super.shutdownNow();
        taskList.addAll(commandThreadpool.shutdownNow());
        return taskList;
    }

    @Override
    public void shutdown() {
        super.shutdown();
        commandThreadpool.shutdown();
    }
}

上面的装饰器可以按以下方式使用:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args){

        long timeout = 2000;

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(true));

        threadPool = new TimeoutThreadPoolDecorator( threadPool ,
                timeout,
                TimeUnit.MILLISECONDS);


        threadPool.execute(command(1000));
        threadPool.execute(command(1500));
        threadPool.execute(command(2100));
        threadPool.execute(command(2001));

        while(threadPool.getActiveCount()>0);
        threadPool.shutdown();


    }

    private static Runnable command(int i) {

        return () -> {
            System.out.println("Running Thread:"+Thread.currentThread().getName());
            System.out.println("Starting command with sleep:"+i);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                System.out.println("Thread "+Thread.currentThread().getName()+" with sleep of "+i+" is Interrupted!!!");
                return;
            }
            System.out.println("Completing Thread "+Thread.currentThread().getName()+" after sleep of "+i);
        };

    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.