当前位置: 首页 > 知识库问答 >
问题:

Future.cancel()后跟Future.get()杀死我的线程

厍兴腾
2023-03-14

我想使用执行器接口(使用Callable),以便启动一个线程(让我们称之为可调用线程),它将执行使用阻塞方法的工作。这意味着当主线程调用Future.cancel(true)(调用Thread.interrupt())时,可调用的线程可以抛出一个InterruptedExc0019。

我还希望我的可调用线程在代码的取消部分使用其他阻塞方法中断时正确终止。

在实现这一点时,我经历了以下行为:当我调用Future.cancel(true)方法时,可调用线程被正确地通知中断,但是如果主线程立即等待它使用Future.get()终止,可调用线程在调用任何阻塞方法时都会被杀死。

下面的JUnit5代码片段说明了这个问题。如果主线程在cancel()和get()调用之间不Hibernate,我们可以很容易地复制它。如果我们睡眠一段时间但睡眠不足,我们可以看到可调用线程完成了其取消工作的一半。如果睡眠充足,可调用线程将正确完成其取消工作。

注1:我检查了可调用线程的中断状态:它被正确地设置了一次,并且只正确地设置了一次,正如预期的那样。

注意2:当调试我的可调用线程一步一步中断后(当传递到取消代码),我松了它后,几个步骤,当进入一个阻塞的方法(没有中断异常似乎被抛出)。

    @Test
    public void testCallable() {

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        System.out.println("Main thread: Submitting callable...");
        final Future<Void> future = executorService.submit(() -> {

            boolean interrupted = Thread.interrupted();

            while (!interrupted) {
                System.out.println("Callable thread: working...");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    System.out.println("Callable thread: Interrupted while sleeping, starting cancellation...");
                    Thread.currentThread().interrupt();
                }
                interrupted = Thread.interrupted();
            }

            final int steps = 5;
            for (int i=0; i<steps; ++i) {
                System.out.println(String.format("Callable thread: Cancelling (step %d/%d)...", i+1, steps));
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    Assertions.fail("Callable thread: Should not be interrupted!");
                }
            }

            return null;
        });

        final int mainThreadSleepBeforeCancelMs = 2000;
        System.out.println(String.format("Main thread: Callable submitted, sleeping %d ms...", mainThreadSleepBeforeCancelMs));

        try {
            Thread.sleep(mainThreadSleepBeforeCancelMs);
        } catch (InterruptedException e) {
            Assertions.fail("Main thread: interrupted while sleeping.");
        }

        System.out.println("Main thread: Cancelling callable...");
        future.cancel(true);
        System.out.println("Main thread: Cancelable just cancelled.");

        // Waiting "manually" helps to test error cases:
        // - Setting to 0 (no wait) will prevent the callable thread to correctly terminate;
        // - Setting to 500 will prevent the callable thread to correctly terminate (but some cancel process is done);
        // - Setting to 1500 will let the callable thread to correctly terminate.
        final int mainThreadSleepBeforeGetMs = 0;
        try {
            Thread.sleep(mainThreadSleepBeforeGetMs);
        } catch (InterruptedException e) {
            Assertions.fail("Main thread: interrupted while sleeping.");
        }

        System.out.println("Main thread: calling future.get()...");
        try {
            future.get();
        } catch (InterruptedException e) {
            System.out.println("Main thread: Future.get() interrupted: Error.");
        } catch (ExecutionException e) {
            System.out.println("Main thread: Future.get() threw an ExecutionException: Error.");
        } catch (CancellationException e) {
            System.out.println("Main thread: Future.get() threw an CancellationException: OK.");
        }

        executorService.shutdown();
    }

共有1个答案

逑阳泽
2023-03-14

当您在取消的Future上调用get()时,您将获得一个C的异常,因此不会等待Callable的代码执行其清除。然后,您刚刚返回,当JUnit确定测试已经完成时,观察到的线程被杀死的行为似乎是JUnit清理的一部分。

要等待完全清理,请将最后一行更改为

executorService.shutdown();

executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);

请注意,在方法的throws子句中声明意外异常比用catch子句调用断言.fail使测试代码混乱更简单。JUnit无论如何都会将此类异常报告为失败。

然后,您可以删除整个睡眠代码。

可能值得将ExecutorService管理置于@之前/@之后甚至@课前/@课后方法中,以保持测试方法不受此影响,并将重点放在实际测试上。ª

这就是朱尼特

 类似资料:
  • 问题内容: 我有以下设置: 我正在创建黄瓜步骤,我需要能够启动和关闭该应用程序。 问题答案: 您可以使用和频道杀死无限循环! 很不错的Go交换,不是吗? 现在,这有什么奇怪的地方?这是一个零尺寸的频道。我们只能用空结构填充它(即:)。可能是a 或其他,因为我们不使用频道的内容。重要的是,我们使用通道通知goroutine中的无限循环,该该停止了。 该语句用于捕获来自渠道的内容。这是一条阻塞语句(除

  • 问题内容: 我有一个简单的程序,可以测试当模块不存在时是否能够引发异常。 有时我喜欢在另一个模块中使用此代码: 令人惊讶的是,当我以这种方式运行它时,它不起作用: 这种情况在Ubuntu中发生,并且在干净的CentOS 7.3中也发生。 问题答案: 您正在遇到“导入锁定”。 该文档提到了线程期间导入的限制,您违反了第一个限制(强调我的意思): 虽然导入机制是线程安全的,但是由于提供线程安全的方式存

  • 本文向大家介绍python杀死一个线程的方法,包括了python杀死一个线程的方法的使用技巧和注意事项,需要的朋友参考一下 最近在项目中遇到这一需求: 我需要一个函数工作,比如远程连接一个端口,远程读取文件等,但是我给的时间有限,比如,4秒钟如果你还没有读取完成或者连接成功,我就不等了,很可能对方已经宕机或者拒绝了。这样可以批量做一些事情而不需要一直等,浪费时间。 结合我的需求,我想到这种办法:

  • 问题内容: 在Java中指定的时间限制后,有没有办法杀死子线程?编辑:此特定线程也可能在最坏的情况下被阻止(线程用于等待文件修改并在发生此事件之前被阻止),所以我不确定中断()是否会成功? 问题答案: 利用来执行,签出你可以指定超时的方法。例如 当然在这里实施。

  • 问题内容: 如何杀死linux中最后一个生成的后台任务? 例: 问题答案: bash中有一个特殊的变量: $!扩展为在后台执行的最后一个进程的PID。

  • 问题内容: 如何java.lang.Thread用Java 杀死A ? 问题答案: 有关他们为何不赞成使用Sun的内容,请参见此主题。它详细介绍了为什么这是一种不好的方法,以及通常应该采取什么措施才能安全地停止线程。 他们建议的方式是使用共享变量作为标志,要求后台线程停止。然后可以由另一个请求线程终止的对象来设置此变量。