Больше, чем сравнение и обмен
Как видно из названия, я ищу реализацию сравнения и замены, но с большим, чем сравнение:
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
тем временем, поэтому мне нужно выполнить цикл и повторить попытку.
Является ли эта реализация правильной (потокобезопасной)?
Ответы
Ответ 1
Я не вижу проблем с вашей реализацией, , если нить никогда не уменьшает значение 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
}
}
Ответ 2
Так как Java 8 можно упростить с помощью updateAndGet:
public boolean greaterThanCAS(int newValue) {
return oldValue.updateAndGet(x -> x < newValue ? newValue : x) == newValue;
}
Обратите внимание, что это вернет true также в случае, когда старые и новые значения равны.
Попробуйте @Adam answer, если это нежелательное поведение.
Ответ 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 говорит, что у меня недостаточно баллов для публикации комментариев. Ваш ответ почти прав, но ваша функция всегда будет возвращать 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;
}