【问题标题】:Does AtomicBoolean not have a negate() method?AtomicBoolean 没有 negate() 方法吗?
【发布时间】:2009-08-10 15:37:11
【问题描述】:

java.util.concurrent.atomic.AtomicBoolean 没有可以原子地否定/反转值的方法吗?我可以用另一种方式吗?我错过了什么吗?

【问题讨论】:

  • 我不明白 boolean b = ab.get(); 有什么问题; ab.compareAndSet(b, !b);你一定会翻转你得到的东西。总是想翻转一个布尔值而不考虑它的当前值真的很常见吗?
  • @Yishai:你肯定会翻转你得到的东西,但假设你有ab=true,并且有 2 个切换器有竞争条件。到达那里的第一个切换位,第二个说“哦,我已经被切换了”,因此不理会它。这将留下ab=false,而如果切换操作单独发生,它将留下ab=true。那是一个错误。至于切换布尔值是否真的很常见:在硬件的情况下很常见,不确定纯软件的情况。
  • @Jason S,我认为您在重申我的问题是肯定的,至少在硬件方面(因此硬件模拟,如果没有别的)你会需要它。但我不知道这是否足以作为 API 的案例。
  • 我有一个乒乓缓冲区,它可以真正使用 AtomicBoolean 中的 getAndNegate() 方法。

标签: java multithreading concurrency


【解决方案1】:

有点老......但并不觉得答案很好。

不得不完全不同意这在硬件中并不常见或仅有用。您可能希望多个线程以相同的可能性切换单个变量......我使用 AtomicLong 来制作一个假布尔值。这是从 JMS MessageListener 中采用的,我需要一半时间响应特定消息,另一半时间响应另一种类型。

public class Mock {
    private static AtomicLong count = new AtomicLong(0);

    public boolean respond() {
        long currentCount = count.getAndIncrement();

        if (currentCount % 2 == 0) {
            return true;
        } else {
            return false;
        }
    }
}

【讨论】:

    【解决方案2】:

    我的幼稚实现是这样的:

    boolean v;
    do {
      v=atomicBoolean.get();
    } while(!atomicBoolean.compareAndSet(v, !v));
    

    【讨论】:

    • 节拍必须同步 :)
    • 不,这不是因为切换不是原子的。 AtomicBoolean 的值可以在调用 get() 和调用 compareAndSet() 之间改变
    • @skaffman:在这种情况下 compareAndSet() 将失败,返回 false 并重新进入循环。如果我有任何错误,请纠正我。
    • 这本质上类似于 AtomicInteger incrementAndGet 的工作方式。 for (;;) { int current = get(); int 下一个 = 当前 + 1; if (compareAndSet(current, next)) 返回下一个; }
    • 为什么不在 AtomicBoolean 中实现?
    【解决方案3】:

    您可以使用AtomicInteger.getAndIncrement() 模拟AtomicBoolean.negate(),并将偶数视为true,将奇数视为false。 数字的实际值应该被忽略,所以你不必关心整数溢出。

    【讨论】:

      【解决方案4】:

      The CERT Oracle Secure Coding Standard for Java一书中建议的解决方案如下:

      import java.util.concurrent.atomic.AtomicBoolean;
      
      final class Flag {
          private AtomicBoolean flag = new AtomicBoolean(true);
      
          public void toggle() {
              boolean temp;
              do {
                  temp = flag.get();
              } while(!flag.compareAndSet(temp, !temp));
          }
      }
      

      【讨论】:

        【解决方案5】:

        使用AtomicBoolean#compareAndSet() 方法和while 循环,您可以实现一个方法以线程安全的方式切换AtomicBoolean 的值,如下所示:

        public static boolean negate(AtomicBoolean ab) {
            // get the oposite value
            boolean newVal = !ab.get();
        
            // try to set the new value if the current value is the oposite of the new value
            while (!ab.compareAndSet(!newVal, newVal)) {
                // if the value we try to set was already set in the mean-time
                // then toggle the new value and try again
                newVal = !newVal;
            }
            // return the value we finally could set
            return newVal;
        }
        

        【讨论】:

        • @jtahlborn 我认为你错了。我对 50 个并发线程进行了测试,试图在每次迭代之间随机等待 0-5 毫秒时切换 AtomicBoolean 10.000 次。所有线程完成。您愿意解释一下您认为这种方法无法完成的情况吗?我的实现与当前最高评价之间的区别在于,我的实现不会在 while 循环的每次迭代中执行无用且昂贵的 #get() 调用,而是切换本地值然后尝试设置它。
        • 另一个解决方案使用当前值,以便它可以选择将其设置为什么。您“猜测”新值应该相反。这可能是对的,也可能是错的。 “从不完整”可能不是一个非常准确的说法。但是,您的版本“猜测”为正确的值(更快成功或虚假失败的可能性),另一个版本使用当前信息选择下一个值(另一个 get 调用但对成功的期望更高)。我想这取决于您喜欢哪种操作模式。
        • @jtahlborn 在 Nicola 的版本中,.get() 和 .compareAndSet() 调用不会在单个原子操作中执行,因此 .get() 的值可能在 .compareAndSet 的那一刻已经过时() 被调用,因此它成为正确值的概率与我只是切换本地值并重试的情况相同,这可以确保每次迭代都有一个易失性读取。
        • 我不确定您如何推断它们具有相同的正确概率。两段代码都可能出错,是的。
        【解决方案6】:

        如果您使用的是 java 9 或更高版本,我建议:

            /**
             * Flip the AtomicBoolean.
             * Sets the boolean value to false if it is true, and to true if it is false
             * with memory effects as specified by {@link java.lang.invoke.VarHandle#setVolatile}.
             *
             * @param atomicBoolean atomicBoolean
             * @return new boolean value of AtomicBoolean
             * @see AtomicInteger#accumulateAndGet(int x, IntBinaryOperator accumulatorFunction)
             * @since 9
             */
            public static final boolean flip(AtomicBoolean atomicBoolean) {
                boolean prev = atomicBoolean.get(), next = false;
                for (boolean haveNext = false; ; ) {
                    if (!haveNext) {
                        next = !prev;
                    }
                    if (atomicBoolean.weakCompareAndSetVolatile(prev, next)) {
                        return next;
                    }
                    haveNext = (prev == (prev = atomicBoolean.get()));
                }
            }
        

        或者如果你想直接得到它...

        https://github.com/XenoAmess/commonx/blob/master/src/main/java/com/xenoamess/commonx/java/util/concurrent/atomic/AtomicBooleanUtilsx.java

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-11
          • 1970-01-01
          • 1970-01-01
          • 2021-01-16
          • 2014-09-22
          • 2012-10-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多