As the title suggests, I'm looking for a compare-and-swap implementation, but with greater-than comparison:
if(newValue > oldValue) { oldValue = newValue; }
where oldValue
is some global shared state and newValue
is private to each thread, without doing this:
synchronized(locker) { if(newValue > oldValue) { oldValue = newValue; } }
because I want a non-blocking solution. From studying source codes of other non-blocking operations, I've come up with this (assuming the values are integers):
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 } }
when // else keep looping
happens, it means that another thread has changed the oldValue
in the meantime and so I need to loop and try again.
Is this implementation correct (thread-safe)?
test-and-set modifies the contents of a memory location and returns its old value as a single atomic operation. compare-and-swap atomically compares the contents of a memory location to a given value and, only if they are the same, modifies the contents of that memory location to a given new value.
Compare and swap is a technique used when designing concurrent algorithms. The approach is to compare the actual value of the variable to the expected value of the variable and if the actual value matches the expected value, then swap the actual value of the variable for the new value passed in.
The compare and the store are atomic: if the store is performed, you are guaranteed that no thread could sneak in and write a value other than old to *ptr. Because a single operation provides the old version of the value and stores a new one, compare-and-swap is said to be an atomic read-modify-write operation.
The compare-and-swap (CAS) instruction is an uninterruptible instruction that reads a memory location, compares the read value with an expected value, and stores a new value in the memory location when the read value matches the expected value. Otherwise, nothing is done.
Since Java 8 this can be simplified with use of updateAndGet:
public boolean greaterThanCAS(int newValue) { return oldValue.updateAndGet(x -> x < newValue ? newValue : x) == newValue; }
Note that this would return true also in case when old and new values are equal. Give a try to @Adam's answer if this is not desired behaviour.
I see no problems with your implementation, provided that no thread ever decreases the value of the AtomicInteger
. If they do, your code is open to race conditions.
Note that the code can be simplified as follows:
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 } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With