[英]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()的進程,否則我認為中斷功能不會卡住。
不,這意味着如果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 */);
它永遠不會回來。 程序可能會凍結。
關鍵是在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.