我理解AtomicInteger和其他原子变量允许并发访问。在什么情况下通常使用这个类?


当前回答

我能想到的最简单的例子是使递增成为一个原子操作。

使用标准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.

其他回答

当我需要为可以从多个线程访问或创建的对象提供id时,我通常使用AtomicInteger,并且我通常将它用作我在对象的构造函数中访问的类的静态属性。

可以在原子整数或长值上使用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的计数器有更好的性能 在低到中等争用情况下,性能优于基于锁的计数器

例如,我有一个生成某些类实例的库。每个实例必须有一个唯一的整数ID,因为这些实例表示发送到服务器的命令,并且每个命令必须有一个唯一的ID。由于允许多个线程并发发送命令,所以我使用AtomicInteger来生成这些id。另一种方法是使用某种锁和常规整数,但这既慢又不优雅。

Atomic classes are not general purpose replacements for java.lang.Integer and related classes. They do not define methods such as equals, hashCode and compareTo. (Because atomic variables are expected to be mutated, they are poor choices for hash table keys.) Additionally, classes are provided only for those types that are commonly useful in intended applications. For example, there is no atomic class for representing byte. In those infrequent cases where you would like to do so, you can use an AtomicInteger to hold byte values, and cast appropriately. You can also hold floats using Float.floatToRawIntBits(float) and Float.intBitsToFloat(int) conversions, and doubles using Double.doubleToRawLongBits(double) and Double.longBitsToDouble(long) conversions.

参考:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html

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).