Recently I was reading a tutorial, in that I came across a statement that says..
"The Java language specification guarantees that reading or writing a variable is an atomic operation(unless the variable is of type long
or double
). Operations variables of type long
or double
are only atomic if they declared with the volatile
keyword."
AtomicInteger
or AtomicLong
that provides methods like getAndDecrement()
, getAndIncrement()
and getAndSet()
which are atomic.
I got confused a bit with the above statement.. could you please clarify when to use AtomicInteger
or AtomicLong
classes.
I think what it means is that long and double - read operation is atomic and write operation is atomic. But a read + write is not atomic.
The above is not thread safe. There read and write are two separate operations. Each of those are guaranteed to be atomic, but the whole expression is not.
To make it thread safe you would need to use an AtomicLong and use the getAndIncrement function.
Atomic means the operation completes without any possibility for something to happen between. eg. getAndDecrement(), on AtomicInteger, guarantees that the variable is returned AND decremented at the same time.
If it was not an atomic operation, the possibility would exist for the value to get decremented (eg. from 3 to 2), then modified by another thread (eg. changing it from 2 to 5), then returned as 5.
You need an
AtomicInteger
if you need to read a variable and write a result depending on the read value. For instance,i++
readsi
(e.g.3
) and writesi+1
(e.g.4
). A thread may be interrupted meanwhile, and three other threads incrementi
too. Now that we get back,i
actually has the value6
but our thread still writes4
, based on what it read beforehand.AtomicInteger.getAndIncrement
ensures you're not interrupted and therefore always incrementing properly. Moreover, the result is always flushed into memory, whereas a non-volatilei
might not be flushed to memory. In this case other threads might not even see the changes.You use int or long based on the upper/lower limit on the range of numbers you are dealing with. Please do not mix non-atomic behavior of long with AtomicLong. Whatever you have written above is correct but you are probably mixing both concepts. AtomicXXX are more useful in cases where you are doing "compare & set" kind of operations. For example even when int can be modified/read atomically following code will be incorrect in multithreaded environment :
in multithread environment two threads can access this code atomically and updated value of i and making it come in consistent state. SO deal with such situations normally you guard the code "if(i == 10) i++;" with synchronized block. However AtomicInteger class provides the API to achieve such things without using synchronized blocks which are slower. Same is the case of AtmoicLong APIs
atomicity of an operation is required when you mutate a variable. doing
int a = 10;
is an atomic operation but its not the one which will give you the problem. the problem giving operations usually are mutating ones likea++
ora = a + 2;
and so on.Java Specification guarantees that 'reading' and 'writing' are atomic operations not their combinations. so an operation which 'reads, adds 1 and then writes the result back' is not atomic as per specification. such operations are called compound operations and they usually need to be atomic in context of their usage in our code.
Atomic types help solve this problem. using incrementAndget() on an atomic type makes 'reads, adds 1 and then writes the result back and reads the new result' a single atomic operation in context to thread safety.
Hope this helps. By the way you should read this (http://walivi.wordpress.com/2013/08/24/concurrency-in-java-a-beginners-introduction/) article about basics of concurrency and threads. it explains such stuff beautifully.
Doing
a = 28
(witha
being anint
) is an atomic operation. But doinga++
is not an atomic operation because it requires a read of the value of a, an incrementation, and a write to a of the result. As a result, if you useda++
to implement a thread-safe counter, you could have two threads reading the value concurrently (26 for example), then have both increment it and writing it concurrently, resulting in 27 as a result, instead of 28.AtomicInteger solves this issue by providing atomic operations like the ones you listed. In my example, you would use
incrementAndGet()
for example, which would guarantee the end value is 28 and not 27.