【问题标题】:How do I get an AtomicReferenceFieldUpdater from a scala companion object?如何从 scala 伴随对象中获取 AtomicReferenceFieldUpdater?
【发布时间】:2011-04-07 00:28:41
【问题描述】:

如果我在某个集合结构中有一个链接节点,我真的不希望它的下一个链接是 AtomicReference(我需要原子 CAS 更新),所以我将其声明为:

@volatile var next: Node[A] = _n

然后在伴随声明中:

val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[Link[_]], classOf[Node[_]], "n")
def cas[A](target: Link[A], old: Node[A], newNode: Node[A]) = updater.compareAndSet(target, old, newNode);

在运行时出现以下错误:

java.lang.RuntimeException: java.lang.IllegalAccessException: 
  Class concurrent.Link$ can not access a member of class concurrent.Link
  with modifiers "private volatile"
    at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.<init>(AtomicReferenceFieldUpdater.java:189)
    at java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdater.java:65)
    at concurrent.Link$.<init>(Link.scala:106)
    ...

因此,在运行时伴随对象是concurrent.Link$ 而不是concurrent.Link,并且不同的类不能访问另一个类的私有成员。

但是,如果我javap -p concurrent.Link

我明白了:

Compiled from "Link.scala"
public final class concurrent.Link implements concurrent.Node,scala.ScalaObject,scala.Product,java.io.Serializable{
private final java.lang.Object value;
private volatile com.atlassian.util.scala.concurrent.Node node;
public static final boolean cas(com.atlassian.util.scala.concurrent.Link, com.atlassian.util.scala.concurrent.Node, com.atlassian.util.scala.concurrent.Node);
public static final java.util.concurrent.atomic.AtomicReferenceFieldUpdater updater();

所以,除了在我的 Link 类上声明的 AtomicReferenceFieldUpdater 的静态实例之外,我什么都有。

问题是,如何在 Scala 中获得一个指向 volatile var 的 AtomicReferenceFieldUpdater 实例?

到目前为止,我发现的唯一方法是返回 Java(使用下一个 Node 字段和静态 AtomicReferenceFieldUpdater 实现 AbstractLink)并从中继承,这很难看。

【问题讨论】:

    标签: scala concurrency java.util.concurrent


    【解决方案1】:

    很难。由于 Scala 将字段设为私有,并且只有访问器方法可用,因此这可能是不可能的。

    在这样做时,我最终决定通过使用 volatile 字段创建 Java 基类并通过那里进行更新来实现。

    Java 文件:

    public class Base {
       volatile Object field = null;
    }
    

    Scala 文件:

    class Cls extends Base {
      val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[Base[_]], classOf[Object[_]], "field")
      def cas[A](old: Object, n: Object) = updater.compareAndSet(this, old, n);
    }
    

    我还没有想出不同的方法。

    【讨论】:

    • 感谢 @axel22,我已经有了丑陋的 Java hack(请参阅问题的最后一行),所以我无法给出这个答案。
    • 实际上,我重新阅读了您的解决方案,它错过了 AtomicReferenceFieldUpdater 的一个重要属性,那就是它是静态的。您希望在所有实例之间共享相同的更新程序,这就是重点——否则您只需直接使用 AtomicReference。
    • 这是一个有待讨论的话题——将其作为私有字段实际上可能比作为静态引用更便宜,并且必须对此进行基准测试。在任何情况下,它都应该比将它放在伴随对象中便宜。
    • 就内存而言,使用静态肯定会更便宜。每个节点只会创建一个对象而不是两个,因此很难看出私有节点会如何更便宜。
    • 在调用 cas 所需的时间方面更便宜。您只创建一次对象,但通常会多次访问它。将更新程序放在伴随对象中意味着要访问它,您需要首先获取伴随对象引用,然后调用其上的 getter 以获取更新程序。拥有一个私有字段相当于一种间接方式。这可以减少一次缓存未命中的成本。而且您总是可以将更新程序实例化一次,并将对它的引用放入所有Cls 对象中。尽管如此,我还是会做一个微基准测试来验证这个假设。
    【解决方案2】:

    有人告诉我,在纯 scala 中无法做到这一点,您需要在 Java 中定义一个类来做到这一点。幸运的是,交叉编译非常简单,但还是很烦人!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-06
      • 2016-02-21
      • 1970-01-01
      相关资源
      最近更新 更多