【问题标题】:Greater-than compare-and-swap大于比较和交换
【发布时间】:2012-03-10 22:17:25
【问题描述】:

正如标题所示,我正在寻找一种比较和交换的实现,但具有大于比较:

if(newValue > oldValue) {
    oldValue = newValue;
}

其中oldValue 是某个全局共享状态,newValue 是每个线程私有的,无需这样做:

synchronized(locker) {
    if(newValue > oldValue) {
        oldValue = newValue;
    }       
}

因为我想要一个非阻塞的解决方案。通过研究其他非阻塞操作的源代码,我想出了这个(假设值是整数):

AtomicInteger oldValue; // shared global variable

...

public boolean GreaterThanCAS(int newValue) {

    while(true) {
        int local = oldValue;
        if(local == oldValue) {
            if(newValue > local) {
                 if(oldValue.compareAndSet(local, newValue) {
                     return true;  // swap successful
                 } // else keep looping
            } else {
                 return false; // swap failed
            }
        } // else keep looping
    }
}

// else keep looping发生时,意味着另一个线程同时更改了oldValue,所以我需要循环再试一次。

这个实现是否正确(线程安全)?

【问题讨论】:

  • 这只是检查分配local变量和检查它们是否相同之间是否发生线程切换。在您的 if 语句之后可能会发生线程切换。所以不,这不是线程安全的,但没有阻塞我不确定你是否会找到解决方案。
  • @Shaded:如果oldValue 不等于localoldValue.compareAndSwap(local, newValue) 调用也会返回 false,因此它也会在此处检查。
  • 您不需要先进行相等比较。只要“if(newValue>local) oldValue.CAS(local, newValue) else repeat”就足够了
  • putIfGreater 或者更好的方法名称。
  • (而oldValue 是一个非常奇怪的名字。希望它不是真正的全球性。)

标签: java multithreading synchronization nonblocking


【解决方案1】:

从 Java 8 开始,这可以通过使用 updateAndGet 来简化:

public boolean greaterThanCAS(int newValue) {
    return oldValue.updateAndGet(x -> x < newValue ? newValue : x) == newValue;
}

请注意,如果旧值和新值相等,这也会返回 true。 如果这不是我们想要的行为,请尝试@Adam's answer

【讨论】:

  • x &lt; newValue ? -newValue : newValue:为什么不是x &lt; newValue ? newValue : x
【解决方案2】:

我认为您的实现没有问题,只要没有线程会降低 AtomicInteger 的值。如果他们这样做了,您的代码将对竞争条件开放。

注意,代码可以简化如下:

public boolean GreaterThanCAS(int newValue) {
    while(true) {
        int local = oldValue.get();
        if(newValue <= local) {
             return false; // swap failed
        }
        if(oldValue.compareAndSet(local, newValue)) {
             return true;  // swap successful
        }
        // keep trying
    }
}

【讨论】:

  • 谢谢。递减会导致问题是对的,但是对于我的场景,oldValue 的值只能通过执行此操作来更改。也感谢您的简化建议。现在想来,if 确实是多余的。
  • 我认为您的代码使用&lt;= 与名称中的GreaterThan 进行比较有点奇怪。
  • @TomHawtin-tackline:我发现这个结构比原来嵌套的if 语句更易读。如果有人反对&lt;=,则可以像if(!(newValue &gt; local)) 一样简单地表述它。我个人认为这个改写后的版本比我在答案中的表述更加清晰。
【解决方案3】:

我会重写它看起来更像:

while(true) {
    int local = oldValue.get();
    if(newValue > local){
       if(oldValue.compareAndSwap(local, newValue) {
              return true;  // swap successful
        } // else keep looping 
    }else 
        return false;
 }

大于检查之前的等价检查是多余的。

否则它应该可以正常工作。

【讨论】:

    【解决方案4】:

    @Vadzim,我会评论你的帖子,但 stackoverflow 说我没有足够的积分来发布 cmets。您的答案几乎是正确的,但是您的函数将始终返回 false,因为 getAndUpdate 始终返回以前的值,或者在您的情况下返回“x”。我认为您需要做的就是将最后一个 '==' 替换为 '

     // return true if the assignment was made, false otherwise
     public boolean greaterThanCAS(int newValue) {
        return oldValue.getAndUpdate(x -> x < newValue ? newValue : x) < newValue;
     }
    

    【讨论】:

    • 感谢您的指出。这个答案也是正确的,但我已经通过切换到updateAndGet 修复了我的问题。请注意,现在答案在处理旧值和新值相等的情况下有所不同。这取决于上下文哪种行为更适合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 2013-10-27
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 2020-11-10
    • 2012-05-04
    相关资源
    最近更新 更多