【发布时间】:2021-11-30 00:50:41
【问题描述】:
在java.lang.invoke 中有两个有趣的结构,MutableCallSite 和SwitchPoint。它们被描述为提供了一种更改目标的方法(对于MutableCallSite,可以更改为任意不同的目标,对于SwitchPoint,可以更改为预定义的无效时目标),其他线程保证可以获取更新,但使用在 MCS 或 SP 未更改时减少了使用 MCS 或 SP 的开销(与在每次使用时检查同步效果相比减少了)。 SwitchPoint 文档说它可以在 MutableCallSite 上构建,并且在 GitHub 上的 OpenJDK 源代码中,它是。所以寻找魔法的地方是MutableCallSite.syncAll。
在the source for that method中,魔法似乎分三步:
- 第 1 步是
lazySet(setRelease的 JMM 效果)在其他线程永远不会读取的私有AtomicInteger上; - 第 2 步查看所有传递的
MutableCallSite引用以确认没有一个为空; - 第三步是评论:
// FIXME: NYI
对于未经训练的人来说,该评论似乎还有很多工作要做。 ;)
Android 的开发人员似乎看到了该评论和 removed the method from Android,因为他们不想宣传未实现的方法(即使 MutableCallSite 没有 syncAll 看起来不像一个有用的结构)。他们说的对吗?
另一方面,它似乎真的不太可能如此明显地缺少它的功能。从 Java 7 开始,它就已经在 Java 中了,而且像 Nashorn 和 JRuby 这样的重要项目已经大量使用了MutableCallSite 和SwitchPoint。我没有偶然发现任何关于它们因此而被破坏的cmets。
此外,我了解 Java 类库中的某些实现可能看起来不完整,因为部分魔法是由 JVM 实现提供的。这似乎是一个可能发生的主要示例,但我无法找到详细信息。
有人知道细节吗?最好知道真正的可靠行为是什么。 syncAll javadoc 一开始听起来很有希望。释义:
- 其作用是强制每个呼叫站点目标的所有未来读者接受最近存储的值
- 它可能(可能??)阻塞,直到所有读者(以某种方式)解除所有先前版本的缓存
- 阅读器线程可能会观察目标的先前版本,直到
syncAll调用返回。 (这是否意味着他们在syncAll调用返回后可能不会这样做?如果是这样,那不是意味着#2 真的比“可能”更强大吗?) - 它“可能很贵”(作为让
getTarget便宜的权衡;这是神奇的部分)。
但稍后在同一个 javadoc 中,深入研究 JMM 细节,它说“如果(其他线程)T 在 volatile 写入之后执行同步操作 A”... 然后 它必须看到更新的目标。
这听起来有点……不那么健壮。
这是否全部基于一些 JVM 实现知识,即在 syncAll 返回后可以花费多长时间的上限?如果是,可以说明这个上限吗?
如果确实没有遗漏任何东西并且事情像宣传的那样工作,是否值得删除源中的// FIXME: NYI(也许用对实际情况的解释来替换它)?
欲了解更多信息:earlier version of this question 已发布在 mlvm-dev 列表中,但没有收到任何回复。
【问题讨论】:
标签: java android concurrency jvm