AtomicInteger для ограниченного поколения последовательности

Как мы можем использовать AtomicInteger для ограниченного поколения последовательности, говорят, что порядковый номер должен быть между 1 - 60. После того как последовательность достигает 60, она должна запуститься снова от 1. Я написал этот код, хотя не совсем уверенный, ориентировано ли это на многопотоковое исполнение или нет?

public int getNextValue()
{
 int v;
 do
 {
   v = val.get();
   if ( v == 60)
   {
    val.set(1);
   }
 }
  while (!val.compareAndSet(v , v + 1));
   return v + 1;
  }
7
задан Cœur 10 December 2017 в 11:57
поделиться

5 ответов

Вы можете сделать

return val.getAndIncrement() % 60;

Если вас не беспокоит превышение целочисленного максимального значения (2147483647). Если это вызывает беспокойство, вы можете взглянуть на реализацию getAndIncrement :

public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return current;
    }
}

Все, что вам нужно изменить, это строку int next ... на что-то вроде:

int next = (current + 1) % 60;

Ой. Это проходит через 0-> 59. Вам нужно 1-> 60, поэтому добавьте единицу к возвращаемому значению, чтобы получить желаемый результат.

15
ответ дан 6 December 2019 в 08:14
поделиться

Нет, это не потокобезопасно - вы не должны вызывать set внутри цикла:

int value, next;
do {
    value = val.get();
    next = (value == 60) ? 1 : (value + 1);
} while (!val.compareAndSet(value, next);
return next;
0
ответ дан 6 December 2019 в 08:14
поделиться

Быстрый ответ, не потокобезопасный. Тест и набор должны быть атомарными, если вы не синхронизируете весь метод. Обратите внимание, что val.get () и проверка v не атомарны.Если поток завершится после v = val.get (), вы получите два вызова с одинаковым порядковым номером.

Кроме того, если compareAndSet не удается, вы никогда не меняете значения, это будет бесконечный цикл.

AtomicInteger имеет getAndIncrement () вызов. Это даст вам чистую ценность для возврата.

Раскрутка немного сложнее. Одно из решений - изменить возвращаемое значение. Примерно так:

int v = val.getAndIncrement();
return (v % 60) + 1;

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

0
ответ дан 6 December 2019 в 08:14
поделиться

Есть ли конкретная причина использовать здесь AtomicInteger вместо простого синхронизированного метода?

Как насчет чего-нибудь простого, например следующего:

private int val=1;

public synchronized int getNextValue() {
 int v=val;
 val = (val==60) ? 1 : (val+1); 
 return v;
}
0
ответ дан 6 December 2019 в 08:14
поделиться

Если вы сделаете метод синхронизированным , то он будет потокобезопасным, пока к val больше не будет доступа. Однако этот подход немного громоздок, я бы переписал его следующим образом:

public synchronized int getNextValue() {
    val.compareAndSet(60, 0); // Set to 0 if current value is 60.
    return val.incrementAndGet();
}

Это дает 1 до 60 назад включительно . Если вам действительно нужно 1 до 59, замените 60 на 59 .

1
ответ дан 6 December 2019 в 08:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: