我理解AtomicInteger和其他原子变量允许并发访问。在什么情况下通常使用这个类?
当前回答
就像gabuzo说的,当我想通过引用传递一个整型时,有时我使用AtomicIntegers。它是一个内置类,具有特定于体系结构的代码,因此它比我可以快速编写的任何MutableInteger更容易,也可能更优化。也就是说,这感觉像是对课程的滥用。
其他回答
AtomicInteger有两个主要用途:
As an atomic counter (incrementAndGet(), etc) that can be used by many threads concurrently As a primitive that supports compare-and-swap instruction (compareAndSet()) to implement non-blocking algorithms. Here is an example of non-blocking random number generator from Brian Göetz's 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; } } } ... } As you can see, it basically works almost the same way as incrementAndGet(), but performs arbitrary calculation (calculateNext()) instead of increment (and processes the result before return).
关键是它们允许安全的并发访问和修改。它们通常在多线程环境中用作计数器——在引入它们之前,这必须是一个用户编写的类,将各种方法包装在同步块中。
我使用AtomicInteger来解决就餐哲学家的问题。
在我的解决方案中,使用AtomicInteger实例来表示fork,每个哲学家需要两个。每个哲学家都被标识为一个整数,从1到5。当一个哲学家使用一个fork时,AtomicInteger保存哲学家的值,从1到5,否则该fork没有被使用,因此AtomicInteger的值为-1。
AtomicInteger允许在一个原子操作中检查一个fork是否空闲,value==-1,如果空闲则将其设置为fork的所有者。参见下面的代码。
AtomicInteger fork0 = neededForks[0];//neededForks is an array that holds the forks needed per Philosopher
AtomicInteger fork1 = neededForks[1];
while(true){
if (Hungry) {
//if fork is free (==-1) then grab it by denoting who took it
if (!fork0.compareAndSet(-1, p) || !fork1.compareAndSet(-1, p)) {
//at least one fork was not succesfully grabbed, release both and try again later
fork0.compareAndSet(p, -1);
fork1.compareAndSet(p, -1);
try {
synchronized (lock) {//sleep and get notified later when a philosopher puts down one fork
lock.wait();//try again later, goes back up the loop
}
} catch (InterruptedException e) {}
} else {
//sucessfully grabbed both forks
transition(fork_l_free_and_fork_r_free);
}
}
}
因为compareAndSet方法不阻塞,它应该增加吞吐量,完成更多的工作。正如你所知道的,Dining Philosophers问题是在需要对资源进行受控访问时使用的,即需要fork,就像一个进程需要资源来继续工作一样。
可以在原子整数或长值上使用compareAndSwap (CAS)实现非阻塞锁。“Tl2”软件事务内存论文这样描述:
我们将一个特殊版本的写锁与每个事务关联起来 内存位置。在其最简单的形式中,版本化写锁是 使用CAS操作获取锁和的单字自旋锁 一个发布它的商店。因为我们只需要一个比特来表示 如果锁已被占用,则使用锁字的其余部分来保存 版本号。
What it is describing is first read the atomic integer. Split this up into an ignored lock-bit and the version number. Attempt to CAS write it as the lock-bit cleared with the current version number to the lock-bit set and the next version number. Loop until you succeed and your are the thread which owns the lock. Unlock by setting the current version number with the lock-bit cleared. The paper describes using the version numbers in the locks to coordinate that threads have a consistent set of reads when they write.
本文介绍了处理器对比较和交换操作的硬件支持,这使得比较和交换操作非常高效。它还声称:
使用原子变量的非阻塞基于cas的计数器有更好的性能 在低到中等争用情况下,性能优于基于锁的计数器
我能想到的最简单的例子是使递增成为一个原子操作。
使用标准int型:
private volatile int counter;
public int getNextUniqueIndex() {
return counter++; // Not atomic, multiple threads could get the same result
}
AtomicInteger:
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
后者是执行简单的突变效果(特别是计数或唯一索引)的一种非常简单的方法,而不必求助于同步所有访问。
More complex synchronization-free logic can be employed by using compareAndSet() as a type of optimistic locking - get the current value, compute result based on this, set this result iff value is still the input used to do the calculation, else start again - but the counting examples are very useful, and I'll often use AtomicIntegers for counting and VM-wide unique generators if there's any hint of multiple threads being involved, because they're so easy to work with I'd almost consider it premature optimisation to use plain ints.
While you can almost always achieve the same synchronization guarantees with ints and appropriate synchronized declarations, the beauty of AtomicInteger is that the thread-safety is built into the actual object itself, rather than you needing to worry about the possible interleavings, and monitors held, of every method that happens to access the int value. It's much harder to accidentally violate threadsafety when calling getAndIncrement() than when returning i++ and remembering (or not) to acquire the correct set of monitors beforehand.
推荐文章
- Intellij IDEA Java类在保存时不能自动编译
- 何时使用Mockito.verify()?
- 在maven中安装mvn到底做什么
- 不可变与不可修改的集合
- 如何在JSON中使用杰克逊更改字段名
- GSON -日期格式
- 如何从线程捕获异常
- 无法解析主机"<URL here>"没有与主机名关联的地址
- 如何在Java中打印二叉树图?
- String.format()在Java中格式化双重格式
- com.jcraft.jsch.JSchException: UnknownHostKey
- Java中的操作符重载
- 如何加速gwt编译器?
- 在Hibernate中重新连接分离对象的正确方法是什么?
- 应该……接住环内还是环外?