【问题标题】:Modular increment with Java's Atomic classes使用 Java 的 Atomic 类进行模块化增量
【发布时间】:2011-01-26 22:38:18
【问题描述】:

令我惊讶的是,Java 的 AtomicInteger 和 AtomicLong 类没有用于模块化增量的方法(因此在达到限制后值会回绕为零)。

我想我必须遗漏一些明显的东西。最好的方法是什么?

例如,我想在线程之间共享一个简单的 int,并且我希望每个线程能够递增它,例如 mod 10。

我可以创建一个使用同步/锁的类,但有没有更好、更简单的方法?

【问题讨论】:

    标签: java modulo atomic


    【解决方案1】:

    只修改 10 读取时的值?

    public class AtomicWrappingCounter {
      private final AtomicLong counter = new AtomicLong();
      private final int max;
    
      public AtomicWrappingCounter(int max) {
        this.max = max;
      }
    
      public int get() {
        return (int) (counter.get() % max);
      }
    
      public int incrementAndGet() {
        return (int) (counter.incrementAndGet() % max);
      }
    }
    

    显然,如果您可能将此计数器增加超过Long.MAX_VALUE 次,则不能使用此方法,但 9 quintillion 是要增加很多次(大约 292 年,以每纳秒 1 次的速度增加!)。

    【讨论】:

    • 这些方法不需要同步吗,ColinD?如果一个线程在incrementAndGet()中,并且增量已经完成,但没有取模,另一个线程调用get()并返回增加但不是取模的值?
    • @Mark:不。模数对于每个线程都是本地的。 AtomicLong 将确保一旦调用了 incrementAndGet(),另一个调用 get() 的线程将看到新值。然后两个线程自行取模,每个线程都会看到预期的最终结果。
    【解决方案2】:

    在 Java 8 中,您可以在 AtomicInteger 中使用 getAndUpdate(和 updateAndGet)。

    例如,如果我们想要一个每次达到 10 时归零的计数器。

    AtomicInteger counter = new AtomicInteger(0);
    
    // to get & update
    counter.getAndUpdate(value -> (value + 1) % 10)
    

    【讨论】:

      【解决方案3】:

      我认为最简单的方法是自己构建一个包装计数器,将其值存储在 AtomicInteger 中,类似于

      public class AtomicWrappingCounter {
          private AtomicInteger value;
          private final int max;
      
          public AtomicWrappingCounter(int start, int max) {
              this.value = new AtomicInteger(start);
              this.max = max;
          }
      
          public int get() {
              return value.get();
          }
      
          /* Simple modification of AtomicInteger.incrementAndGet() */
          public int incrementAndGet() {
              for (;;) {
                  int current = get();
                  int next = (current + 1) % max;
                  if (value.compareAndSet(current, next))
                      return next;
              }
          }
      }
      

      为什么AtomicInteger 本身不提供类似的东西?谁知道呢,但我认为并发框架作者的意图是提供一些构建块,您可以使用它们来更好地创建自己的更高级别的函数。

      【讨论】:

      • 他们只需要在框架中实现getcompareAndSet。所有其他方法都可以建立在这些之上。
      • 聪明——但我怀疑在激烈的争论下这实际上会比同步更糟糕。
      • @Michael 你能详细说明为什么吗?会不会和 AtomicInteger 自己的行为有什么不同?
      • @Michael:问题是,incrementAndGet() 的实现方式和他这里的方法一样,只是没有 mod。
      • 对,所以在这种情况下,您无论如何都不想使用原子类,这种情况基本上否定了这个问题的核心。不过要知道的好信息。
      【解决方案4】:

      addModular() 方法中添加synchronized 修饰符或块有什么困难?

      Atomic 类没有此功能的原因是它们基于当前 CPU 提供的specific atomic hardware instructions,如果不求助于锁定或其他更复杂且潜在的像 matt 建议的那样低效的算法。

      【讨论】:

        【解决方案5】:

        我很惊讶 Java 的 AtomicInteger 和 AtomicLong 类没有用于模块化增量的方法。

        当标准类不包含支持各种不寻常用例的“花里胡哨”时,不要感到惊讶。设计师必须在他们包含的内容和不包含的内容上划清界限。趋势是支持常见的用例和任何其他方式都无法支持的用例。在这种情况下,这些标准都不适用。

        【讨论】:

          猜你喜欢
          • 2021-12-17
          • 2016-08-14
          • 2017-04-13
          • 1970-01-01
          • 2016-12-10
          • 1970-01-01
          • 2014-06-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多