I sort of understand that AtomicInteger and other Atomic variables allow concurrent accesses. In what cases is this class typically used though?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
- Difference between Types.INTEGER and Types.NULL in
The absolute simplest example I can think of is to make incrementing an atomic operation.
With standard ints:
With AtomicInteger:
The latter is a very simple way to perform simple mutations effects (especially counting, or unique-indexing), without having to resort to synchronizing all access.
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 useAtomicIntegers
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 plainints
.While you can almost always achieve the same synchronization guarantees with
ints
and appropriatesynchronized
declarations, the beauty ofAtomicInteger
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 theint
value. It's much harder to accidentally violate threadsafety when callinggetAndIncrement()
than when returningi++
and remembering (or not) to acquire the correct set of monitors beforehand.There are two main uses of
AtomicInteger
:As an atomic counter (
incrementAndGet()
, etc) that can be used by many threads concurrentlyAs 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:
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).For example, I have a library that generates instances of some class. Each of these instances must have a unique integer ID, as these instances represent commands being sent to a server, and each command must have a unique ID. Since multiple threads are allowed to send commands concurrently, I use an AtomicInteger to generate those IDs. An alternative approach would be to use some sort of lock and a regular integer, but that's both slower and less elegant.
I used AtomicInteger to solve the Dining Philosopher's problem.
In my solution, AtomicInteger instances were used to represent the forks, there are two needed per philosopher. Each Philosopher is identified as an integer, 1 through 5. When a fork is used by a philosopher, the AtomicInteger holds the value of the philosopher, 1 through 5, otherwise the fork is not being used so the value of the AtomicInteger is -1.
The AtomicInteger then allows to check if a fork is free, value==-1, and set it to the owner of the fork if free, in one atomic operation. See code below.
Because the compareAndSet method does not block, it should increase throughput, more work done. As you may know, the Dining Philosophers problem is used when controlled accessed to resources is needed, i.e. forks, are needed, like a process needs resources to continue doing work.
Like gabuzo said, sometimes I use AtomicIntegers when I want to pass an int by reference. It's a built-in class that has architecture-specific code, so it's easier and likely more optimized than any MutableInteger I could quickly code up. That said, it feels like an abuse of the class.
The primary use of
AtomicInteger
is when you are in a multithreaded context and you need to perform thread safe operations on an integer without usingsynchronized
. The assignation and retrieval on the primitive typeint
are already atomic butAtomicInteger
comes with many operations which are not atomic onint
.The simplest are the
getAndXXX
orxXXAndGet
. For instancegetAndIncrement()
is an atomic equivalent toi++
which is not atomic because it is actually a short cut for three operations: retrieval, addition and assignation.compareAndSet
is very useful to implements semaphores, locks, latches, etc.Using the
AtomicInteger
is faster and more readable than performing the same using synchronization.A simple test:
On my PC with Java 1.6 the atomic test runs in 3 seconds while the synchronized one runs in about 5.5 seconds. The problem here is that the operation to synchronize (
notAtomic++
) is really short. So the cost of the synchronization is really important compared to the operation.Beside atomicity AtomicInteger can be use as a mutable version of
Integer
for instance inMap
s as values.