AtomicLong
и AtomicInteger
используют CAS для внутреннего использования — сравнивают и устанавливают (или сравнивают и меняют местами). Идея состоит в том, что вы сообщаете CAS две вещи: ожидаемое значение long/int и значение, до которого вы хотите его обновить. Если long/int имеет значение, которое, как вы говорите, должно быть, CAS автоматически произведет обновление и вернет true
; в противном случае обновление не будет выполнено и будет возвращено false
. Многие современные чипы очень эффективно поддерживают CAS на уровне машинного кода; если JVM работает в среде, в которой нет CAS, она может использовать мьютексы (то, что Java называет синхронизацией) для реализации CAS. В любом случае, если у вас есть CAS, вы можете безопасно реализовать атомарное приращение с помощью этой логики (в псевдокоде):
long incrementAndGet(atomicLong, byIncrement)
do
oldValue = atomicLong.get() // 1
newValue = oldValue + byIncrement
while ! atomicLong.cas(oldValue, newValue) // 2
return newValue
Если другой поток войдет и сделает свое собственное приращение между строками // 1
и // 2
, CAS потерпит неудачу, и цикл повторит попытку. В противном случае CAS сработает.
В таком подходе есть авантюра: если конкуренция низкая, CAS работает быстрее, чем синхронизированный блок, и вероятность переключения контекста потока меньше. Но если есть много конфликтов, некоторым потокам придется пройти через несколько итераций цикла для каждого шага, что, очевидно, равносильно напрасной работе. Вообще говоря, incrementAndGet будет работать быстрее при наиболее распространенных нагрузках.
person
yshavit
schedule
20.12.2011
ho
может иметь некоторые проблемы, что, кстати, указано в самом коде сTODO
. - person denis.solonenko   schedule 20.12.2011