2012-02-20 62 views
12

正如标题所暗示的,我正在寻找一个比较并交换实现,但大于比较:大于比较并交换

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在此期间和所以我需要循环并重试。

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

+0

这只是检查是否分配'local'变量和检查,看看之间发生线程切换如果他们是一样的。线程切换可能发生在if语句之后。所以不,这不是线程安全的,但没有阻止我不确定你是否会找到解决方案。 – Shaded 2012-02-20 15:20:24

+0

@Shaded:如果'oldValue'不等于'local','oldValue.compareAndSwap(local,newValue)'调用也会返回false,所以它也会在这里检查。 – Tudor 2012-02-20 15:22:17

+0

你不需要第一次平等comparizon。只是“如果(newValue> local)oldValue.CAS(local,newValue)else repeat”就足够了 – BegemoT 2012-02-20 15:23:07

回答

7

我认为没有任何问题与您的实施,前提是没有线程曾减少的价值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 
    } 
} 
+0

谢谢。你说得对,递减会导致问题,但对于我的情况,'oldValue'的值只能通过执行这个操作来改变。也感谢简化建议。现在我想到了,这个“如果”确实是多余的。 – Tudor 2012-02-20 15:29:45

+0

我觉得你的代码使用'<='与名称中的'GreaterThan'方法进行比较有点奇怪。 – 2012-02-20 16:47:52

+0

@ TomHawtin-tackline:我发现这个结构的可读性比原来嵌套的if语句更可读。如果有人对'<='进行反向操作,那么可以简单地将其作为“if(!(newValue> local))”。我个人并没有发现这个改编版本比我在答案中写得更清楚或更不清楚。 – NPE 2012-02-20 16:49:50

2

我会重新写它看起来更像是:

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

的等价性检查比前检查是多余越大。

否则它应该工作正常。

10

由于Java 8这可以通过使用updateAndGet被简化:

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

注意,这会也返回true的情况下,当新旧值相等。 如果这不是预期的行为,试试@Adam's answer

+3

'x hengxin 2015-12-27 06:19:26

+0

@hengxin,谢谢,修正。 – Vadzim 2017-01-04 19:22:43

+0

应该使用'updateAndGet'来代替吗? – 2017-07-18 08:54:04

2

@Vadzim,我会评论你的帖子,但stackoverflow说我没有足够的要点发表评论。你的答案几乎是正确的,但是你的函数总是返回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; 
} 
+0

谢谢指出。这个答案也是正确的,但我已经用 修改了我的'updateAndGet'。请注意,现在的答案在处理旧值和新值相等的情况下会有所不同。这取决于哪种行为更适合上下文。 – Vadzim 2017-07-18 10:40:49