Подтвердить что ты не робот

Больше, чем сравнение и обмен

Как видно из названия, я ищу реализацию сравнения и замены, но с большим, чем сравнение:

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 тем временем, поэтому мне нужно выполнить цикл и повторить попытку.

Является ли эта реализация правильной (потокобезопасной)?

4b9b3361

Ответ 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;
 }