【问题标题】:Real life use and explanation of the AtomicLongFieldUpdate classAtomicLongFieldUpdate 类的实际使用和解释
【发布时间】:2013-06-21 16:00:16
【问题描述】:

有人知道AtomicLongFieldUpdate 类在现实生活中的用途吗? 我已经阅读了描述,但我并没有完全理解它的含义。 为什么我想知道这个?好奇心和 OCPJP 准备。

提前致谢。

【问题讨论】:

  • 这个太老了,不知道能不能编辑。但是你们有没有注意到这个大错字,即使在答案中?该课程称为AtomicLongFieldUpdater,而不是AtomicLongFieldUpdate! :)

标签: java multithreading atomic ocpjp


【解决方案1】:

您可以考虑以下成本阶梯:

  • 普通long:便宜,但多线程访问不安全
  • volatile long:更昂贵,多线程访问安全,不可能进行原子操作
  • AtomicLong:最昂贵,多线程访问安全,可能的原子操作

(当我说“不安全”或“不可能”时,我的意思当然是“没有像同步这样的外部机制”。)

如果需要多线程访问,但大多数操作是简单的读取或写入,只需要少量原子操作,您可以创建一个AtomicLongFieldUpdate 的静态实例,并在需要原子更新时使用它。内存/运行时开销类似于简单的volatile 变量,除了原子操作与普通AtomicLong 操作相比(或略贵)。

这是nice little tutorial

【讨论】:

  • 只是为了添加更多信息。 AtomicLong 仅比 volatile long 更“昂贵”,因为它有一个包装类——因此内存开销略多。此外,volatile long 不是“安全的多线程访问”,如果您正在执行 ++
  • AtomicLongFieldUpdate 的内存开销也将远远超过简单的AtomicLong。而且运行时会比volatile longAtomicLong 都慢,因为它使用了反射。
  • @Gray 您可以使用 1 个静态 ALFU 实例,因此内存开销并不像您想象的那么大。另外,我认为(不确定)反射仅用于设置字段,而不是每次访问;加上最近的 HotSpot 版本,操作是intrinsified。确切的成本模型(内存和运行时)将取决于 JRE 和 JVM 实现。
  • 您对 1 个静态实例是正确的。反射 必须 用于每次访问,否则 1 static 将不起作用@rxg。
  • @Gray 我看过 Oracle JDK 中的实现。根据底层 JVM 的能力,有两种不同的实现(一种使用同步,另一种不锁定),但两者都在其构造函数中执行最昂贵的反射工作,将真正的 compareAndSet 委托给 sun.misc.Unsafe - 所以设置更昂贵,但我预计运行时成本会比普通的 AtomicLong 高一点。
【解决方案2】:

你会使用的原因,例如AtomicLongFieldUpdater 赞成 AtomicLong 只是为了降低堆成本。在内部,两者在 compareAndSet 级别上的工作几乎相同,最后都使用 sun.misc.Unsafe。

假设您有一个初始化 1000k 次的特定类。使用 AtomicLong,您将创建 1000k AtomicLong。另一方面,使用 AtomicLongFieldUpdater,您将创建 1 个 CONSTANT AtomicLongFieldUpdater 和 1000k 长的原语,这当然不需要那么多堆空间。

【讨论】:

    【解决方案3】:

    有人知道AtomicLongFieldUpdate 类在现实生活中的任何用途吗?

    我自己从未使用过这个类,但在我的工作空间上使用 get 时,我看到了几个“现实生活”中的使用实例:

    • com.google.common.util.concurrent.AtomicDouble 使用它以原子方式修改其内部volatile long 字段,该字段使用Number.doubleToRawLongBits(...) 存储来自double 的位。很酷。

    • net.sf.ehcache.Element 使用它自动更新hitCount 字段。

    我已经阅读了描述,但我并没有完全理解它的含义。

    它基本上提供与AtomicLong 相同的功能,但在另一个类的本地字段上。 AtomicLongFieldUpdate 的内存负载低于 AtomicLong,因为您为每个字段配置一个更新实例,因此内存开销更低,但反射带来的 CPU 开销更多(尽管可能很小)。

    javadocs 说:

    此类设计用于原子数据结构,其中同一节点的多个字段独立地受到原子更新的影响。

    当然,但我会使用多个 Atomic* 字段。我使用该类的唯一原因是,如果有一个我无法更改的现有类,我想以原子方式递增。

    【讨论】:

      【解决方案4】:

      当然。我最近一直在阅读Alibaba Druid。我发现AtomicLongFieldUpdater 在这个项目中被广泛使用。

      
      // stats
          private volatile long                    recycleErrorCount         = 0L;
          private volatile long                    connectErrorCount         = 0L;
      protected static final AtomicLongFieldUpdater<DruidDataSource> recycleErrorCountUpdater
                  = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "recycleErrorCount");
          protected static final AtomicLongFieldUpdater<DruidDataSource> connectErrorCountUpdater
                  = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "connectErrorCount");
      
      

      如上所述,属性recycleErrorCountconnectErrorCount 用于计算错误发生次数。 相当多的DataSource(包含上述属性的类)将在应用程序生命周期内创建,在这种情况下,使用ALFU 比使用AtomicLong 明显减少堆空间消耗。

      【讨论】:

        【解决方案5】:

        原子通常用于并行编程。

        work-stealing模式下,只支持async、finish、forasync、isolated、atomic变量。

        您可以将原子视为一种安全的保护措施,可以避免数据竞争和并行编程中需要关注的其他问题。

        【讨论】:

        • 这并没有回答专门关于AtomicLongFieldUpdate的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-30
        • 1970-01-01
        • 1970-01-01
        • 2018-04-05
        • 2021-06-11
        • 2021-09-01
        相关资源
        最近更新 更多