【问题标题】:FutureTask get() method may distable LockSupport.parkFutureTask get() 方法可能会禁用 LockSupport.park
【发布时间】:2021-07-11 02:26:35
【问题描述】:

我发现 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.执行线程(线程池)完成任务,解除所有等待者;

3.getting thread(main) read state,发现task hash完成,然后返回结果跳过执行FutureTask locksupport.park()

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

这是一个错误吗?

【问题讨论】:

  • “distable”似乎不是一个词。进一步注意,不需要像LockSupport.park(new Object()); 那样指定无意义的对象。如果无法提供有意义的对象进行监控,就使用LockSupport.park();。

标签: java java-8 concurrency java.util.concurrent futuretask


【解决方案1】:

The documentation 确实说:

除非许可可用,否则禁用当前线程以进行线程调度。

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2012-04-23
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 2020-11-05
    • 2013-08-20
    相关资源
    最近更新 更多