I was surprised that Java's AtomicInteger and AtomicLong classes don't have methods for modular increments (so that the value wraps around to zero after hitting a limit).
I figure I've got to be missing something obvious. What's the best way to do this?
For example, I want to share a simple int between threads, and I want ea开发者_如何学编程ch thread to be able to increment it, say, mod 10.
I can create a class which uses synchronization/locks, but is there a better, easier way?
Just mod 10 the value when you read from it?
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);
}
}
Obviously if you might increment this counter more than Long.MAX_VALUE
times, you couldn't use this approach, but 9 quintillion is a lot of times to be incrementing (around 292 years at a rate of 1 per nanosecond!).
In Java 8 you can use getAndUpdate (and updateAndGet
) in AtomicInteger
.
For example if we want to have a counter that wraps to zero each time it hits 10.
AtomicInteger counter = new AtomicInteger(0);
// to get & update
counter.getAndUpdate(value -> (value + 1) % 10)
I would think the simplest way is to build a wrapping counter yourself which stores it's values in an AtomicInteger, something like
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;
}
}
}
Why doesn't AtomicInteger
provide something like this itself? Who knows, but I think the intention of the concurrency framework authors were to provide some building blocks you could use to better create your own higher-level functions.
What's difficult about adding a synchronized
modifier or block to your addModular()
method?
The reason why the Atomic
classes don't have this functionality is that they're based on specific atomic hardware instructions offered by current CPUs, and modular arithmetic cannot be implemented by those without resorting to locking or other more complex and potentially inefficient algorithms like the one suggested by matt.
精彩评论