2011-01-26 6 views
2

Я был удивлен тем, что классы Java AtomicInteger и AtomicLong не имеют методов модульных приращений (так что значение обнуляется до нуля после достижения предела).Модульное приращение с атомными классами Java

Я полагаю, что мне не хватает чего-то очевидного. Каков наилучший способ сделать это?

Например, я хочу поделиться простым Int между потоками, и я хочу, чтобы каждый поток, чтобы иметь возможность увеличить его, скажем, по модулю 10.

я могу создать класс, который использует синхронизацию/блокировку, но есть ли лучший, более простой способ?

ответ

4

Что не так сложно добавить модификатор или блок synchronized к вашему методу addModular()?

Причина, почему Atomic классы не имеют эту функцию является то, что они основаны на specific atomic hardware instructions предлагаемых текущих процессоры, и модульная арифметика не может быть реализована теми, не прибегая к фиксации или другим более сложным и потенциально неэффективным алгоритмам, таким как предложенный матом.

+0

Спасибо, Майкл, и все остальные за объяснения и реализации. – Mark

11

Только мод 10 значение, когда вы читаете его?

public class AtomicWrappingCounter { 
    private final AtomicLong counter = new AtomicLong(); 
    private final int max; 

    public AtomicWrappingCounter(int max) { 
    this.max = max; 
    } 

    public int get() { 
    return (int) (counter.get() % max); 
    } 

    public int incrementAndGet() { 
    return (int) (counter.incrementAndGet() % max); 
    } 
} 

Очевидно, что если вы можете увеличивать этот счетчик более Long.MAX_VALUE раз, вы не могли бы использовать этот подход, но 9 нониллиона много раз, чтобы быть приращения (около 292 лет при ставке 1 наносекунды!).

+0

Не нужно ли эти методы синхронизировать, ColinD? Что делать, если один поток находится внутри incrementAndGet(), и инкремент завершен, но не по модулю, а другой поток вызывает get() и возвращает значение с добавленным, но не модульным значением? – Mark

+1

@Mark: Нет. По модулю локально для каждого потока. «AtomicLong» будет гарантировать, что как только 'incrementAndGet()' был вызван на него, другой поток, вызывающий 'get()', увидит новое значение. Оба потока затем по модулю значения сами по себе, и каждый видит ожидаемый конечный результат. – ColinD

8

Я думаю, самый простой способ заключается в создании оберточной счетчик себя, который хранит его значения в AtomicInteger, что-то вроде

public class AtomicWrappingCounter { 
    private AtomicInteger value; 
    private final int max; 

    public AtomicWrappingCounter(int start, int max) { 
     this.value = new AtomicInteger(start); 
     this.max = max; 
    } 

    public int get() { 
     return value.get(); 
    } 

    /* Simple modification of AtomicInteger.incrementAndGet() */ 
    public int incrementAndGet() { 
     for (;;) { 
      int current = get(); 
      int next = (current + 1) % max; 
      if (value.compareAndSet(current, next)) 
       return next; 
     } 
    } 
} 

Почему не AtomicInteger давать что-то вроде этого сам? Кто знает, но я думаю, что намерение авторов параллельной структуры было предоставить некоторые строительные блоки, которые вы могли бы использовать, чтобы лучше создавать свои собственные функции более высокого уровня.

+0

Им действительно нужно реализовать 'get' и' compareAndSet' в рамках. На них можно построить все другие методы. – finnw

+1

Умный - но я подозреваю, что под действием взвинчивания это на самом деле будет хуже, чем синхронизация. –

+0

@ Майкл, можете ли вы понять почему? Будет ли это иначе, чем собственное поведение AtomicInteger? –

1

Я был удивлен тем, что классы Java AtomicInteger и AtomicLong не имеют методов для модульных приращений.

Не удивляйтесь, когда стандартный класс не включает в себя «колокола и свистки» для поддержки всех видов необычных прецедентов. Дизайнеры должны рисовать линию где-то на том, что они включают, и что они не включают. Тенденция заключается в поддержке общих случаев использования и случаев использования, которые не могут поддерживаться каким-либо другим способом. В этом случае ни один из этих критериев не применяется.