Atomic Operations and multithreading

2019-03-09 01:13发布

问题:

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.

回答1:

Doing a = 28 (with a being an int) is an atomic operation. But doing a++ 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 used a++ 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.



回答2:

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.



回答3:

You need an AtomicInteger if you need to read a variable and write a result depending on the read value. For instance, i++ reads i (e.g. 3) and writes i+1 (e.g. 4). A thread may be interrupted meanwhile, and three other threads increment i too. Now that we get back, i actually has the value 6 but our thread still writes 4, 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-volatile i might not be flushed to memory. In this case other threads might not even see the changes.



回答4:

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.

volatile long num;
num = num+1

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.



回答5:

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 :

int i =10
..
..
..
if(i == 10) i++;

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



回答6:

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 like a++ or a = 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.