Jam*_* P. 214 java concurrency atomic
我有点理解AtomicInteger和其他Atomic变量允许并发访问.在什么情况下这个类通常使用?
axt*_*avt 182
有两个主要用途AtomicInteger:
作为原子计数器(incrementAndGet()等)可以被许多线程同时使用
作为支持比较和交换指令(compareAndSet())实现非阻塞算法的原语.
以下是BrianGöetz的Java Concurrency In Practice中的非阻塞随机数生成器示例:
public class AtomicPseudoRandom extends PseudoRandom {
private AtomicInteger seed;
AtomicPseudoRandom(int seed) {
this.seed = new AtomicInteger(seed);
}
public int nextInt(int n) {
while (true) {
int s = seed.get();
int nextSeed = calculateNext(s);
if (seed.compareAndSet(s, nextSeed)) {
int remainder = s % n;
return remainder > 0 ? remainder : remainder + n;
}
}
}
...
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它基本上与几乎相同incrementAndGet(),但执行任意计算(calculateNext())而不是增量(并在返回之前处理结果).
And*_*yle 96
我能想到的绝对最简单的例子是增加原子操作.
标准整数:
private volatile int counter;
public int getNextUniqueIndex() {
return counter++; // Not atomic, multiple threads could get the same result
}
Run Code Online (Sandbox Code Playgroud)
使用AtomicInteger:
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
Run Code Online (Sandbox Code Playgroud)
后者是执行简单突变效果(特别是计数或唯一索引)的一种非常简单的方法,而不必求助于同步所有访问.
通过使用compareAndSet()乐观锁定类型可以使用更复杂的无同步逻辑- 获取当前值,基于此计算结果,如果值仍然是用于进行计算的输入,则设置此结果,否则重新开始 - 但是计数示例非常有用,AtomicIntegers如果有任何涉及多个线程的提示,我会经常使用计数和VM范围的唯一生成器,因为它们很容易使用我几乎认为使用普通的过早优化ints.
虽然你几乎总能用ints相应的synchronized声明来实现相同的同步保证,但美妙之AtomicInteger处在于线程安全性内置于实际对象本身,而不是你需要担心每种方法可能的交错和监视器.恰好访问该int值.在呼叫时getAndIncrement()比在返回i++和记住(或不记住)事先获取正确的监视器组时更容易违反线程安全.
Pow*_*ord 56
如果你看一下AtomicInteger的方法,你会发现它们倾向于对应于整数的常见操作.例如:
static AtomicInteger i;
// Later, in a thread
int current = i.incrementAndGet();
Run Code Online (Sandbox Code Playgroud)
是这个的线程安全版本:
static int i;
// Later, in a thread
int current = ++i;
Run Code Online (Sandbox Code Playgroud)
该方法映射是这样的:
++i是i.incrementAndGet()
i++是i.getAndIncrement()
--i是i.decrementAndGet()
i--是i.getAndDecrement()
i = x是i.set(x)
x = i是x = i.get()
还有其他便利方法,如compareAndSet或addAndGet
gab*_*uzo 36
主要用途AtomicInteger是当您处于多线程上下文中时,您需要在不使用的情况下对整数执行线程安全操作synchronized.原始类型的赋值和检索int已经是原子的,但是AtomicInteger带有许多非原子的操作int.
最简单的是getAndXXX或xXXAndGet.例如getAndIncrement(),原子等价物i++不是原子的,因为它实际上是三个操作的捷径:检索,添加和赋值.compareAndSet对于实现信号量,锁,锁存等非常有用.
使用AtomicInteger比使用同步执行相同操作更快,更可读.
一个简单的测试:
public synchronized int incrementNotAtomic() {
return notAtomic++;
}
public void performTestNotAtomic() {
final long start = System.currentTimeMillis();
for (int i = 0 ; i < NUM ; i++) {
incrementNotAtomic();
}
System.out.println("Not atomic: "+(System.currentTimeMillis() - start));
}
public void performTestAtomic() {
final long start = System.currentTimeMillis();
for (int i = 0 ; i < NUM ; i++) {
atomic.getAndIncrement();
}
System.out.println("Atomic: "+(System.currentTimeMillis() - start));
}
Run Code Online (Sandbox Code Playgroud)
在使用Java 1.6的PC上,原子测试在3秒内运行,而同步测试在大约5.5秒内运行.这里的问题是synchronize(notAtomic++)的操作非常短.因此,与操作相比,同步的成本非常重要.
除了原子性之外,AtomicInteger可以Integer用作例如Maps值的可变版本.
Ser*_*nov 16
例如,我有一个库,可以生成某个类的实例.这些实例中的每一个都必须具有唯一的整数ID,因为这些实例表示发送到服务器的命令,并且每个命令必须具有唯一的ID.由于允许多个线程同时发送命令,因此我使用AtomicInteger生成这些ID.另一种方法是使用某种锁和常规整数,但这既慢又不优雅.
就像gabuzo说的那样,当我想通过引用传递一个int时,我有时会使用AtomicIntegers.它是一个内置类,具有特定于体系结构的代码,因此它比我可以快速编写代码的任何MutableInteger更容易,也可能更优化.也就是说,这感觉就像滥用课堂一样.
小智 7
在Java 8中,原子类已经扩展了两个有趣的函数:
两者都使用updateFunction来执行原子值的更新.区别在于第一个返回旧值而第二个返回新值.可以实现updateFunction以执行比标准操作更复杂的"比较和设置"操作.例如,它可以检查原子计数器是否低于零,通常需要同步,这里的代码是无锁的:
public class Counter {
private final AtomicInteger number;
public Counter(int number) {
this.number = new AtomicInteger(number);
}
/** @return true if still can decrease */
public boolean dec() {
// updateAndGet(fn) executed atomically:
return number.updateAndGet(n -> (n > 0) ? n - 1 : n) > 0;
}
}
Run Code Online (Sandbox Code Playgroud)
代码取自Java Atomic Example.
| 归档时间: |
|
| 查看次数: |
181594 次 |
| 最近记录: |