future.cancel 无效
future.cancel does not work
我有一个漂亮而紧凑的代码,但它没有像我预期的那样工作。
public class Test {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
for (;;) {
}
} finally {
System.out.println("FINALLY");
}
}
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(r);
try {
future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
boolean c = future.cancel(true);
System.out.println("Timeout " + c);
} catch (InterruptedException | ExecutionException e) {
System.out.println("interrupted");
}
System.out.println("END");
}
}
输出为:
Timeout true
END
问题:
为什么不终止调用的Runnable 的future.cancel(true) 方法?
程序将 "END" 写入输出后,"r" Runnable 仍然是 运行.
这总是有点误导:ExceutorService 甚至底层线程调度程序对 Runnable 正在做什么一无所知。在您的情况下,他们不知道存在无条件循环。
所有这些方法(cancel,done,...)都与管理Executor结构中的线程有关。 cancel
从 Executor 服务的角度取消线程。
程序员必须测试 Runnable 是否被取消并且必须终止 run()
方法。
所以在你的情况下(如果我没记错的话)是这样的:
public class Test {
public static void main(String[] args) {
FutureTask r = new FutureTask () {
@Override
public void run() {
try {
for (;!isCancelled();) {
}
} finally {
System.out.println("FINALLY");
}
}
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(r);
try {
future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
boolean c = future.cancel(true);
System.out.println("Timeout " + c);
} catch (InterruptedException | ExecutionException e) {
System.out.println("interrupted");
}
System.out.println("END");
}
}
当您取消 Runnable
已经开始的 Future
时,interrupt
方法在 运行 Thread
上调用 Runnable
。但这不一定会停止线程。事实上,如果它陷入一个紧密的循环中,就像你在这里得到的那样,Thread
将不会停止。在这种情况下,interrupt
方法只是设置一个名为 "interrupt status" 的标志,它告诉线程在可以停止时停止。
问题是你的Runnable是不可中断的:任务中断是Java中的一个协作过程,被取消的代码需要定期检查是否被取消,否则不会响应中断。
您可以按如下方式修改代码,它应该会按预期工作:
Runnable r = new Runnable() {
@Override public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {}
} finally {
System.out.println("FINALLY");
}
}
};
Future.cancel() 将取消任何排队的任务或将在您的线程上调用 Thread.interrupt()如果已经 运行.
您需要中断您的代码
您的代码有责任为任何中断做好准备。我想说的是,只要你有一个很长的 运行 任务,你就可以像这样插入一些中断就绪代码:
while (... something long...) {
... do something long
if (Thread.interrupted()) {
... stop doing what I'm doing...
}
}
如何停止我正在做的事情?
您有多种选择:
- 如果您在 Runnable.run() 中,只需 return 或跳出循环并完成方法。
- 您可能在代码深处的其他方法中。那时该方法抛出 InterruptedException 可能是有意义的,因此您只需这样做(清除标志)。
- 但也许在您的代码深处抛出 InterruptedException 没有意义。在那种情况下,您应该抛出一些其他异常,但在此之前标记您的线程再次中断,以便捕获的代码知道中断正在进行中。这是一个例子:
private void someMethodDeepDown() {
while (.. long running task .. ) {
... do lots of work ...
if (Thread.interrupted()) {
// oh no! an interrupt!
Thread.currentThread().interrupt();
throw new SomeOtherException();
}
}
}
现在异常可以传播并终止线程或被捕获,但接收代码有望注意到正在进行中断。
我有一个漂亮而紧凑的代码,但它没有像我预期的那样工作。
public class Test {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
for (;;) {
}
} finally {
System.out.println("FINALLY");
}
}
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(r);
try {
future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
boolean c = future.cancel(true);
System.out.println("Timeout " + c);
} catch (InterruptedException | ExecutionException e) {
System.out.println("interrupted");
}
System.out.println("END");
}
}
输出为:
Timeout true
END
问题: 为什么不终止调用的Runnable 的future.cancel(true) 方法? 程序将 "END" 写入输出后,"r" Runnable 仍然是 运行.
这总是有点误导:ExceutorService 甚至底层线程调度程序对 Runnable 正在做什么一无所知。在您的情况下,他们不知道存在无条件循环。
所有这些方法(cancel,done,...)都与管理Executor结构中的线程有关。 cancel
从 Executor 服务的角度取消线程。
程序员必须测试 Runnable 是否被取消并且必须终止 run()
方法。
所以在你的情况下(如果我没记错的话)是这样的:
public class Test {
public static void main(String[] args) {
FutureTask r = new FutureTask () {
@Override
public void run() {
try {
for (;!isCancelled();) {
}
} finally {
System.out.println("FINALLY");
}
}
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(r);
try {
future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
boolean c = future.cancel(true);
System.out.println("Timeout " + c);
} catch (InterruptedException | ExecutionException e) {
System.out.println("interrupted");
}
System.out.println("END");
}
}
当您取消 Runnable
已经开始的 Future
时,interrupt
方法在 运行 Thread
上调用 Runnable
。但这不一定会停止线程。事实上,如果它陷入一个紧密的循环中,就像你在这里得到的那样,Thread
将不会停止。在这种情况下,interrupt
方法只是设置一个名为 "interrupt status" 的标志,它告诉线程在可以停止时停止。
问题是你的Runnable是不可中断的:任务中断是Java中的一个协作过程,被取消的代码需要定期检查是否被取消,否则不会响应中断。
您可以按如下方式修改代码,它应该会按预期工作:
Runnable r = new Runnable() {
@Override public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {}
} finally {
System.out.println("FINALLY");
}
}
};
Future.cancel() 将取消任何排队的任务或将在您的线程上调用 Thread.interrupt()如果已经 运行.
您需要中断您的代码
您的代码有责任为任何中断做好准备。我想说的是,只要你有一个很长的 运行 任务,你就可以像这样插入一些中断就绪代码:
while (... something long...) {
... do something long
if (Thread.interrupted()) {
... stop doing what I'm doing...
}
}
如何停止我正在做的事情?
您有多种选择:
- 如果您在 Runnable.run() 中,只需 return 或跳出循环并完成方法。
- 您可能在代码深处的其他方法中。那时该方法抛出 InterruptedException 可能是有意义的,因此您只需这样做(清除标志)。
- 但也许在您的代码深处抛出 InterruptedException 没有意义。在那种情况下,您应该抛出一些其他异常,但在此之前标记您的线程再次中断,以便捕获的代码知道中断正在进行中。这是一个例子:
private void someMethodDeepDown() {
while (.. long running task .. ) {
... do lots of work ...
if (Thread.interrupted()) {
// oh no! an interrupt!
Thread.currentThread().interrupt();
throw new SomeOtherException();
}
}
}
现在异常可以传播并终止线程或被捕获,但接收代码有望注意到正在进行中断。