FutureTask get() 方法可能会失效 LockSupport.park

FutureTask get() method may distable 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 在等待者中添加获取线程(main);

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

3.getting thread(main) 读取状态并发现任务散列完成,然后 return 结果并跳过执行 FutureTask locksupport.park()

4.because unpark 方法在 futuretask 中执行,然后可以跳过 LockSupport.park(new Object()); 并打印 error,unPark

这是一个错误吗?

The documentation 确实说:

Disables the current thread for thread scheduling purposes unless the permit is available.

If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes disabled for thread scheduling purposes and lies dormant until one of three things happens:

  • Some other thread invokes unpark with the current thread as the target; or
  • Some other thread interrupts the current thread; or
  • The call spuriously (that is, for no reason) returns.

This method does not report which of these caused the method to return. Callers should re-check the conditions which caused the thread to park in the first place. Callers may also determine, for example, the interrupt status of the thread upon return.

仅第三个项目符号就足以告诉您,您不能假设 returning from park 意味着您正在等待的条件已经满足。

通常,此工具适用于无法为 checks/induces 条件和 park/unpark 调用的操作假设原子性的代码。

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

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

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