微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

FutureTask get() 方法可能会禁用 LockSupport.park

如何解决FutureTask get() 方法可能会禁用 LockSupport.park

我发现 FutureTask get() 方法可能会禁用 oracle jdk8 中的 LockSupport.park

我的代码是:

        ExecutorService service = Executors.newFixedThreadPool(1,(r) -> {
            Thread thread = new Thread(r);
            thread.setDaemon(true);
            return thread;
        });

        Future<String> submit = service.submit(() -> {
            TimeUnit.SECONDS.sleep(5);
            System.out.println("exec future task..");
            return "a";
        });

        System.out.println(submit.get());

        LockSupport.park(new Object());
        System.out.println("error,unPark");
    }

我以为System.out.println("error,unPark");不会执行;但它确实执行了

exec future task..
a
error,unPark

为了模拟线程调度,我在 FutureTask 行 418 处断点

queued = UNSAFE.compareAndSwapObject(this,waitersOffset,q.next = waiters,q);

在打印exec future task..之前快速跨过

打印exec future task..一段时间后,继续执行..

然后跳过LockSupport.park(new Object());并打印error,unPark

我认为

1.FutureTask在waiters中添加获取线程(main);

2.执行线程(线程池)完成任务,unpark所有waiters;

3.getting thread(main)读取状态,发现task hash完成,返回结果,跳过执行FutureTask locksupport.park()

4.因为在futuretask中执行了unpark方法,所以可以跳过LockSupport.park(new Object());并打印error,unPark

这是一个错误吗?

解决方法

The documentation 确实说:

除非许可可用,否则出于线程调度目的禁用当前线程。

如果许可可用,则它被消耗并且调用立即返回;否则当前线程会因线程调度目的而被禁用,并处于休眠状态,直到发生以下三种情况之一:

  • 其他一些线程以当前线程为目标调用unpark;或
  • 一些其他线程 interrupts 当前线程;或
  • 调用虚假(即无缘无故)返回。

此方法报告哪些导致方法返回。调用者应该首先重新检查导致线程停放的条件。调用者还可以确定,例如,返回时线程的中断状态。

仅第三点就足以告诉您,您不能假设从 park 返回意味着您正在等待的条件已经满足。

通常,此工具适用于无法为检查/诱导条件和 park/unpark 调用的操作假定原子性的代码。

根据文档的结论,您必须在从 park 返回后重新检查条件。特别强调“re-”;因为这意味着您可能会发现 park 的返回不是由于满足您的条件,所以您可能消耗了不适合您的 unpark。这反过来意味着,您还必须在调用 park 之前测试条件,并且在条件已经满足时不要调用它。但是,如果您这样做,您可能会在调用 park 时跳过 unpark,因此某些后续的 park 调用将立即返回。

简而言之,即使没有“虚假返回”,您始终必须在调用 park 之前测试您的特定条件,并在从 park 返回后重新检查条件,如果您想在循环中等待条件。

请注意,其中大部分内容也适用于在 wait 块中使用 notify/synchronized 或拥有 {{1} }.两者都应与预测试循环一起使用,以获得可靠的结果。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。