如何解决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 举报,一经查实,本站将立刻删除。