繁体   English   中英

中断无响应的线程

[英]Interrupting a unresponsive thread

从“实践中的Java并发性”这本书中,有一条语句-> 如果任务对中断没有响应,则timedRun将不会返回,直到任务完成为止,这可能会在所需的超时之后很长时间

private static final ScheduledExecutorService cancelExec = ...;

public static void timedRun(Runnable r,
                           long timeout, TimeUnit unit) {
    final Thread taskThread = Thread.currentThread();
    cancelExec.schedule(new Runnable() {
        public void run() { taskThread.interrupt(); }
    }, timeout, unit);
    r.run();
}

这是否意味着interrupt()函数可能会卡住? 可能导致interrupt()卡住的原因。 它只是设置目标线程的isInterrupted标志。 我预见除非并且直到任何进程可能饿死正在调用interrupt()的进程,否则我认为中断功能不会卡住。

不,这意味着如果rrun期间未检查中断,则中断它不会做任何事情。

这种方法的结构非常复杂。

public static void timedRun(Runnable r,
                       long timeout, TimeUnit unit) {

    // reference to the thread calling the method
    final Thread taskThread = Thread.currentThread();

    cancelExec.schedule(new Runnable() {
        public void run() {               // another thread is
            taskThread.interrupt();       // scheduled to interrupt
        }                                 // the calling thread later
    }, timeout, unit);

    r.run(); // run a long running task on the calling thread
             // this is where interrupt may or may not be seen later
             // this is the "task" the blurb is referring to
}

如果我像下面这样调用此方法:

timedRun(new Runnable() {
    @Override
    public void run() {
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(2000L);
        } catch(InterruptedException e) {}
        System.out.println(System.currentTimeMillis() - start);
    }
}, 1L, TimeUnit.SECONDS);

它将输出大约1000因为中断会导致在睡眠期间引发异常。

如果我这样做:

timedRun(new Runnable() {
    @Override
    public void run() {
        while(!Thread.interrupted()) {
            /* do whatever */
        }
    }
}, 1L, TimeUnit.SECONDS);

大约1秒钟后,它也会看到中断,因为我正在检查它。

如果我这样做:

timedRun(new Runnable() {
    @Override
    public void run() {
        while(true);
    }
}, /* doesn't matter */, /* doesn't matter */);

它永远不会回来。 程序可能会冻结。

关键是在r.run()返回之前, timedRun无法返回。 如果Runnable r忽略了它已被中断的事实,则r.run()可能确实返回得很晚。 不是interrupt调用被卡住(它可能几乎立即完成),而是中断可以被其目标忽略,从而阻止timedRun完成,直到run方法到达其自然结束(如果有)。

另一个解决方案可以是,另一个线程执行长时间运行的任务,而调用者线程(调用“ timedRun”的线程)等待任务完成,如下所示:

/**
     * Runs a long running task with a timer.
     * 
     * @param longRunningTask long running task
     * @param timeout as milliseconds, method will return if long running task is not     completed by this time
     * @throws InterruptedException
     */
    void timedRun(Runnable longRunningTask, long timeout)
            throws InterruptedException {

        // Thread that executes runnable
        Thread newThread = new Thread(longRunningTask);
        newThread.start();

        // Current thread joins the new thread with a timeout
        Thread.currentThread().join(timeout);

        // Time expired, longRunningTask might be completed or not
        newThread.interrupt();  //optional

    }

这里的整个混乱点是作者在方法timedRun的参数中引入了参数Runnable r。

我只是想通过删除该参数并引入一个新任务来简化它。

让我们考虑一下我们的TimedRun类是如下重新创建的

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TimedRun {
public static final ScheduledExecutorService cancelExec = new   ScheduledThreadPoolExecutor(
        1);

public static void timedRun(long timeout, TimeUnit unit) {

    final Thread taskThread = Thread.currentThread();
    System.out.println(taskThread);
    cancelExec.schedule(new Runnable() {
        public void run() {
            taskThread.interrupt();
        }
    }, timeout, unit);

    runAnytask();
}

private static void runAnytask() {

    System.out.println(Thread.currentThread());
        while (true) {
            // do some work here infinitely
}

}

然后让我们创建方法timedRun的调用方,如下所示

导入java.util.concurrent.TimeUnit;

public class TimedRunCaller {
public static void main(String[] args) {
TimedRun.timedRun(1L, TimeUnit.SECONDS);
System.out.println("Now it should terminate");
TimedRun.cancelExec.shutdown();
}

}

作者在这里要说的是,如果task(在本例中为runAnytask方法)对中断没有响应,那么使用runAnytask作为任务的timedRun方法将不会返回给调用者,直到task(runAnytask)完成。但是请注意,这里的runAnytask方法无限循环,因此它将永远不会结束。

尽管timedRun方法正在中断调用者线程(这里的timedRun方法的调用者是主线程),但是runAnytask方法没有机制来响应该中断。因此timedRun方法将永远不会返回给调用者。

但是,如果我们按以下方式修改runAnytask

private static void runAnytask() {

    System.out.println(Thread.currentThread());
    long start = System.currentTimeMillis();
    try {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("interupted");
                break;
            }
        }
    } catch (Exception e) {
        System.out.println("interuptrd "
                + (System.currentTimeMillis() - start));
    }

} 

我们可以看到现在任务正在响应中断,它将响应timedRun方法引发的中断,并在指定的超时后返回给调用方。

因此作者说您应该了解规则:在中断之前您应该了解线程的中断策略,无论它是设计用来响应中断还是不响应中断,否则您的中断就不会被注意到,就像我们在第一种情况下的runAnytask方法一样。

我希望它现在清除一切。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM