如何超时一个线程

jav*_*eek 246 java multithreading timeout timer

我想在一段固定的时间内运行一个线程.如果它没有在那段时间内完成,我想杀死它,抛出一些异常,或以某种方式处理它.怎么做到呢?

我从这个线程 中发现的一种方法是在Thread的run()方法中使用TimerTask.

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

 
编辑:添加赏金,因为我需要一个更清晰的答案.下面给出的ExecutorService代码没有解决我的问题.为什么我应该在执行后睡觉()(一些代码 - 我没有处理这段代码)?如果代码完成并且sleep()被中断,那怎么可能是timeOut?

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

Bal*_*usC 361

事实上,而使用ExecutorService替代的Timer,这里是一个SSCCE:

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!";
    }
}
Run Code Online (Sandbox Code Playgroud)

使用方法中的timeout参数进行一些操作Future#get(),例如将其增加到5,您将看到线程完成.您可以拦截catch (TimeoutException e)块中的超时.

更新:澄清一个概念的误解,将sleep()必需的.它仅用于SSCCE /演示目的.只需在那里完成长期运行的任务sleep().在长时间运行的任务中,您应该检查线程是否未被中断,如下所示:

while (!Thread.interrupted()) {
    // Do your long running task here.
}
Run Code Online (Sandbox Code Playgroud)

  • 将`Thread.sleep(4000)`替换为其他一些长时间运行的语句,该示例将不起作用.换句话说,如果`Task`被设计为理解`Thread.isInterrupted()`状态改变,那么这个例子只能****. (22认同)

eri*_*son 46

对于任何旧任务,没有100%可靠的方法来执行此操作.任务必须考虑到这种能力.

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

当然,如果你的任务是一个简单的循环,你可以只检查每次迭代的当前时间,并在指定的超时时间结束时放弃.在这种情况下不需要工作线程.


Dre*_*lls 13

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

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


Pet*_*eng 8

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

从上面提到的Java 文档:

如果一个线程没有响应Thread.interrupt怎么办?

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

底线:

确保所有线程都可以被中断,否则您需要具体的线程知识 - 比如设置一个标志.也许您可以要求将任务与停止它所需的代码一起提供给您 - 使用stop()方法定义接口.您也可以在未能停止任务时发出警告.


use*_*503 8

BalusC说:

更新:为了澄清概念上的误解,不需要sleep().它仅用于SSCCE /演示目的.只是在那里做长期运行的任务而不是睡觉().

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

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

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

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


Die*_*ter 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;
            }

        }

    }
}
Run Code Online (Sandbox Code Playgroud)

它被称为这样:

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) {}  
Run Code Online (Sandbox Code Playgroud)