2012-02-20 65 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