中断无响应的线程

时间:2013-12-28 06:21:24

标签: java multithreading

从“实践中的java并发”一书中,有一个声明 - > if the task is not responsive to interruption, timedRun will not return until the task finishes, which may be long after the desired timeout

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()函数可能会卡住?什么可能导致中断()卡住。它只是设置目标线程的isInterrupted标志。我预见除非并且直到任何进程可能使正在调用interrupt()的进程饿死,我认为中断函数不会卡住。

4 个答案:

答案 0 :(得分:2)

不,这意味着如果r没有以某种方式检查run期间的中断,则中断它将无法执行任何操作。

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

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 */);

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

另一个解决方案可能是,另一个线程执行长时间运行的任务,调用程序线程(调用“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

    }

答案 3 :(得分:0)

这里混淆的一点是作者在方法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的调用者,如下所示

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

}

这里作者想说,如果任务(在我们的例子中是方法runAnytask)没有响应中断,则使用此runAnytask作为任务的timedRun方法将不会返回给调用者任务(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方法中那样被忽视。 / p>

我希望现在能清除所有内容。

相关问题