Я кодировал следующую версию nextValue
, используя compareAndSet
, которая предназначена для использования в несинхронизированном блоке.Он прошел ваши юнит-тесты:
О, и я ввел новые константы для MIN_VALUE и MAX_VALUE, но вы можете игнорировать их, если хотите.
static final int LOWEST_VALUE = Byte.MIN_VALUE;
static final int HIGHEST_VALUE = Byte.MAX_VALUE;
private AtomicInteger counter = new AtomicInteger(LOWEST_VALUE - 1);
private AtomicInteger resetCounter = new AtomicInteger(0);
public byte nextValue() {
int oldValue;
int newValue;
do {
oldValue = counter.get();
if (oldValue >= HIGHEST_VALUE) {
newValue = LOWEST_VALUE;
resetCounter.incrementAndGet();
if (isSlow) slowDownAndLog(10, "resetting");
} else {
newValue = oldValue + 1;
if (isSlow) slowDownAndLog(1, "missed");
}
} while (!counter.compareAndSet(oldValue, newValue));
return (byte) newValue;
}
compareAndSet()
работает в сочетании с get()
для управления параллелизмом.
В начале критической секции вы выполняете get()
, чтобы получить старое значение.Затем вы выполняете некоторую функцию, зависящую только от старого значения, чтобы вычислить новое значение.Затем вы используете compareAndSet()
, чтобы установить новое значение.Если AtomicInteger больше не равен старому значению во время выполнения compareAndSet()
(из-за одновременной активности), он завершается неудачно, и вы должны начать заново.
Если у вас экстремальное количество параллелизма иВремя вычислений велико, вполне возможно, что compareAndSet()
может много раз потерпеть неудачу, прежде чем преуспеет, и, возможно, стоит собрать статистику по этому вопросу, если вас это касается.
Я не предполагаю, что это лучше или хужеподход, чем простой синхронизированный блок, как предлагали другие, но я лично, вероятно, для простоты использовал бы синхронизированный блок.
РЕДАКТИРОВАТЬ : я отвечу на ваш фактический вопрос "Почему не мойработа? "
Ваш код имеет:
int next = counter.incrementAndGet();
if (next > Byte.MAX_VALUE) {
Поскольку эти две строки не защищены синхронизированным блоком, несколько потоков могут выполнять их одновременно, и все получают значения next
> Byte.MAX_VALUE
.Все они затем перейдут в синхронизированный блок и установят counter
обратно на INITIAL_VALUE
(один за другим, пока они ждут друг друга).
За прошедшие годы было написано огромное количествоиз-за ловушек попыток настроить производительность, не синхронизируя, когда это не кажется необходимым.Например, см. Двойная проверка блокировки