Больше чем сравнивать и менять - PullRequest
16 голосов
/ 20 февраля 2012

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

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

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

Ответы [ 4 ]

14 голосов
/ 07 декабря 2014

Начиная с Java 8 это можно упростить с помощью updateAndGet :

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

Обратите внимание, что это вернет true также в случае, когда старые и новые значения равны.Попробуйте @ ответ Адама , если это нежелательное поведение.

8 голосов
/ 20 февраля 2012

Я не вижу проблем с вашей реализацией, при условии, что ни один поток не уменьшает значение 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 голосов
/ 03 февраля 2017

@ Вадим, я бы прокомментировал ваше сообщение, но stackoverflow говорит, что у меня недостаточно очков, чтобы оставлять комментарии.Ваш ответ почти верен, но ваша функция всегда будет возвращать false, потому что getAndUpdate всегда возвращает предыдущее значение или «x» в вашем случае.Я думаю, все, что вам нужно сделать, это заменить ваш последний '==' на '<', например: </p>

 // return true if the assignment was made, false otherwise
 public boolean greaterThanCAS(int newValue) {
    return oldValue.getAndUpdate(x -> x < newValue ? newValue : x) < newValue;
 }
2 голосов
/ 20 февраля 2012

Я бы переписал его так, чтобы он выглядел так:

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

Проверка эквивалентности перед проверкой больше, чем избыточна.

В противном случае все должно работать нормально.

...