簡體   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