[英]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.