我有以下代码:
public class Cancelling {
public static void main(String args[]) {
ToBeCancelled tbc = new ToBeCancelled();
ForkJoinPool pool = new ForkJoinPool(1);
Future<?> future = pool.submit(tbc);
try {
Thread.sleep(3000);
} catch (InterruptedException ie) {}
future.cancel(true);
if (future.isCancelled())
System.out.println("The task has been cancelled");
}
}
ToBeCancelled
类是:
public class ToBeCancelled implements Runnable {
public void run() {
try {
Thread.sleep(5000); // should throw exception here
} catch (Exception e) {
return; // should exit
}
System.out.println("I should never be able to print this");
}
}
主线程应该启动,等待3秒,然后使用ToBeCancelled
取消future.cancel(true)
任务。然后它应该打印The task has been cancelled
,而任务永远不会打印它的消息。
至少,当我从控制台启动它时会发生这种情况。
当我从具有TextArea的GUI应用程序启动它时,输出被重定向到,但事实并非如此。主要方法是打印The task has been cancelled
,但任务也会打印I should never be able to print this
。
这让我疯了。根据我的理解,任务应该在cancel
方法上接收Thread.sleep(5000)
命令,这将触发因此被捕获的异常并使线程返回。但它没有发生,但主要认为它已被取消。这就像任务完全忽略cancel
方法一样。
我已经尝试了我能想到的一切,检查cancel
的返回值,使用Thread.currentThread().isInterrupted()
让任务等待更长时间,但没有任何效果。
我觉得我错过了一些非常简单的东西,但我找不到它是什么。有什么想法吗?
如果有人认为它可能是GUI应用程序中的某些内容,则这是启动程序的方法:
public static void StartProgram(String name) {
try {
Method m = Class.forName(name).getDeclaredMethod("main",String[].class);
Object[] args = new Object[1];
String s[] = new String[2];
s[0] = tf1.getText();
s[1] = tf2.getText();
args[0] = s;
t = new Thread(new ProgramStarter(args, m));
t.start();
} catch (Exception e) {
e.printStackTrace();
}
}
ProgramStarter
为:
public class ProgramStarter implements Runnable {
private Object[] args;
private Method m;
public ProgramStarter(Object args[], Method m) {
this.args = args;
this.m = m;
}
public void run() {
try {
m.invoke(null, args);
} catch (Exception e) {
e.printStackTrace();
}
}
}
答案 0 :(得分:1)
问题是您的验证错误。您认为您的代码在从控制台运行时有效,但事实上,它在所有情况下都会失败。从控制台运行时,主线程在尝试取消未来后结束,JVM将终止,因为JVM中只剩下守护程序线程。由于JVM终止,您没有注意到取消不起作用。
在sleep
方法的末尾添加main
以延迟JVM终止时,您会注意到从控制台运行时也会打印"I should never be able to print this"
。因此,GUI和控制台版本之间的唯一区别是运行的事件调度线程阻止JVM终止,因此您发现它不起作用。
底线是:不要使用 ForkJoinPool
,除非您有理由。
由于您只需要submit
到一个简单的单背景线程执行程序,您可以使用Executors.newFixedThreadPool(1)
创建执行程序。这意外行为较少:默认情况下,它的线程是非守护进程,而Future
将cancel
按预期中断。