【发布时间】:2011-01-26 22:38:18
【问题描述】:
令我惊讶的是,Java 的 AtomicInteger 和 AtomicLong 类没有用于模块化增量的方法(因此在达到限制后值会回绕为零)。
我想我必须遗漏一些明显的东西。最好的方法是什么?
例如,我想在线程之间共享一个简单的 int,并且我希望每个线程能够递增它,例如 mod 10。
我可以创建一个使用同步/锁的类,但有没有更好、更简单的方法?
【问题讨论】:
令我惊讶的是,Java 的 AtomicInteger 和 AtomicLong 类没有用于模块化增量的方法(因此在达到限制后值会回绕为零)。
我想我必须遗漏一些明显的东西。最好的方法是什么?
例如,我想在线程之间共享一个简单的 int,并且我希望每个线程能够递增它,例如 mod 10。
我可以创建一个使用同步/锁的类,但有没有更好、更简单的方法?
【问题讨论】:
只修改 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 quintillion 是要增加很多次(大约 292 年,以每纳秒 1 次的速度增加!)。
【讨论】:
AtomicLong 将确保一旦调用了 incrementAndGet(),另一个调用 get() 的线程将看到新值。然后两个线程自行取模,每个线程都会看到预期的最终结果。
在 Java 8 中,您可以在 AtomicInteger 中使用 getAndUpdate(和 updateAndGet)。
例如,如果我们想要一个每次达到 10 时归零的计数器。
AtomicInteger counter = new AtomicInteger(0);
// to get & update
counter.getAndUpdate(value -> (value + 1) % 10)
【讨论】:
我认为最简单的方法是自己构建一个包装计数器,将其值存储在 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 本身不提供类似的东西?谁知道呢,但我认为并发框架作者的意图是提供一些构建块,您可以使用它们来更好地创建自己的更高级别的函数。
【讨论】:
get和compareAndSet。所有其他方法都可以建立在这些之上。
incrementAndGet() 的实现方式和他这里的方法一样,只是没有 mod。
在addModular() 方法中添加synchronized 修饰符或块有什么困难?
Atomic 类没有此功能的原因是它们基于当前 CPU 提供的specific atomic hardware instructions,如果不求助于锁定或其他更复杂且潜在的像 matt 建议的那样低效的算法。
【讨论】:
我很惊讶 Java 的 AtomicInteger 和 AtomicLong 类没有用于模块化增量的方法。
当标准类不包含支持各种不寻常用例的“花里胡哨”时,不要感到惊讶。设计师必须在他们包含的内容和不包含的内容上划清界限。趋势是支持常见的用例和任何其他方式都无法支持的用例。在这种情况下,这些标准都不适用。
【讨论】: