执行者服务的未来任务未真正取消

2022-09-04 01:11:24

我将我的未来从执行器服务推送到哈希映射中。稍后,我可能会从哈希映射中对期货进行取消。尽管结果是正确的,但我后来在可调用过程中遇到了断点,就好像 Future cancel() 没有效果一样。我认为这可能是两个不同引用的情况(即使引用ID在断点时被列为相同),但想知道一些专家是否可以加入进来。代码如下所示:

ExecutorService taskExecutor = Executors.newCachedThreadPool();
Map <String, Future<Object>> results = new HashMap <String, Future<Object>>();      

Future<Object> future = taskExecutor.submit(new MyProcessor(uid));
results.put(uid, future);

我允许处理继续(这是一个在传入任务时提交任务的循环),稍后我可能会尝试通过调用此方法从外部源取消:

public static synchronized boolean cancelThread(String uid) {
    Future<Object> future = results.get(uid);
    boolean success = false;
    if (future != null) {
        success = (future.isDone() ? true : future.cancel(true));
        if (success)
            results.remove(uid);
    }
    return success;     
}

但是在 future.cancel() 被调用后,我仍然在 MyProcessor.call() 中遇到一个“未取消”的路径 - 即它并没有真正被取消。

我哪里出错了?有没有更好的方法来做到这一点?


答案 1

我后来在可调用过程中命中了断点,就好像 Future cancel() 没有效果一样。

Future.cancel(true)删除队列中尚未运行的作业,但如果该作业已在运行,则相当于在线程上运行该作业。这将设置线程上的中断位,并导致任何 、 和一些其他方法抛出 。Thread.interrupt()sleep()wait()InterruptedException

重要的是要意识到它不会停止线程。您需要主动检查线程循环中的中断标志或正确处理 。InterruptedException

有关更多详细信息,请参阅此处的SO答案:如何使用线程的id挂起线程?


答案 2

FutureTask :: boolean cancel(boolean mayInterruptIfRunning)将在当前正在运行的线程上执行。interrupt

FutureTask.java
public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();     ////////////HERE/////////////
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

JavaDoc说下面interrupt

公共 void interrupt()

中断此线程。除非当前线程正在中断自身(这始终是允许的),否则将调用此线程的 checkAccess 方法,这可能会导致引发 SecurityException。

如果在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者调用该类的 join()、join(long)、join(long、int)、sleep(long)或 sleep(long, int) 方法时阻塞此线程,则其中断状态将被清除,并且它将收到一个 InterruptedException。

如果此线程在可中断通道上的 I/O 操作中被阻塞,则该通道将关闭,线程的中断状态将被设置,并且该线程将收到 ClosedByInterruptException。

如果此线程在选择器中被阻塞,则将设置线程的中断状态,并且它将立即从选择操作返回,可能具有非零值,就像调用选择器的唤醒方法一样。

如果上述条件均不成立,则将设置此线程的中断状态。

中断不活跃的线程不需要产生任何影响。

抛出:安全异常 - 如果当前线程无法修改此线程

总结;取消 仅在线程被阻止时才有影响(在调用 wait(),...)时,否则开发人员有责任检查退出;同时执行非阻塞操作。FutureTaskThread.currentThread().isInterrupted()


推荐