2010-06-02 103 views
7

我们如何使用AtomicInteger进行有限的序列生成,例如序列号必须在1到60之间。一旦序列达到60,它必须从1开始重新开始。我写了这段代码,虽然不太确定这是线程安全还是不安全?AtomicInteger用于有限序列生成

public int getNextValue() 
{ 
int v; 
do 
{ 
    v = val.get(); 
    if (v == 60) 
    { 
    val.set(1); 
    } 
} 
    while (!val.compareAndSet(v , v + 1)); 
    return v + 1; 
    } 

回答

14

你可以做

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,所以添加一个返回值来获得所需的结果。

+0

+1。这真的很有用 – satish 2010-06-02 18:10:24

+0

如果你找到一个真正有用的答案,点击复选标记接受它。 – naiad 2010-06-02 18:19:23

+1

奇怪的实现。基本上它说“如果还没有改变就加1,否则继续尝试”。难道这不能在理论上导致无限循环? – 2010-09-29 20:16:02

0

不,这不是线程安全的 - 你不应该叫set一个周期内:

int value, next; 
do { 
    value = val.get(); 
    next = (value == 60) ? 1 : (value + 1); 
} while (!val.compareAndSet(value, next); 
return next; 
1

如果您的方法​​那么这将是线程安全的,只要val无处访问。该方法是不过有点麻烦,我想如下改写:

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

这使1至60回包容。如果您实际需要1直到59,则请用59替换60

0

在这里使用AtomicInteger而不仅仅是一个简单的同步方法的任何特定原因?

怎么样简单的东西像下面这样:

private int val=1; 

public synchronized int getNextValue() { 
int v=val; 
val = (val==60) ? 1 : (val+1); 
return v; 
} 
+0

p.s.没有任何反对AtomicIntegers,但我认为,为了长期可维护性,尽可能简化事情总是很好的... – mikera 2010-06-02 17:40:07

+0

感谢您的回复。我正在寻找阻塞算法的非阻塞算法。 – satish 2010-06-02 18:13:34

+0

如果存在大量争用(高达10倍),AtomicInteger比同步快得多。 – starblue 2010-06-02 19:21:12

0

快速的答案,不是线程安全的。测试和集合需要是原子的,除非您使整个方法同步。注意val.get()和v的测试不是原子的。如果线程在v = val.get()之后返回,您将得到两个具有相同序列号的调用。另外,如果compareAndSet失败,您永远不会更改这些值,它将是一个无限循环。

AtomicInteger有一个 getAndIncrement() 调用。这会让你获得一个干净的价值来回报。

滚动有点棘手。一种解决方法是修改返回值。像这样:

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

由于每个线程都有v的本地副本,所以我们可以安全地对它做一些数学运算并返回值。如果发生溢出,有一个棘手的问题。根据您生成序列号的频率,这可能是也可能不是问题。

+0

这一代的频率相当高,大概每50毫秒。 – satish 2010-06-02 18:08:24

+0

每秒20个呼叫(每50ms打一个呼叫),大约3。4年后你会得到翻转。可能不是太糟糕,取决于你的应用程序。如果这将是一个问题,一个同步块可能是最简单的解决方案。在你疯狂地优化它之前,确保这个代码是一个真正的瓶颈。我怀疑有比每50毫秒运行一次的任务更低的挂果。 – 2010-06-02 19:20:23

3

您可以使用Java 8在一行中完成此操作。

AtomicInteger counter = new AtomicInteger(); 

public int getNextValue() { 
    return counter.updateAndGet(n -> (n >= 60) ? 1 : n + 1); 
}