How is atomicity achieved in classes defined in the java.util.concurrent.atomic package?

I looked at the source code of java.util.concurrent.atomic.AtomicInteger to find out how atomicity is achieved through the atomic operations provided by the class. For example, the source AtomicInteger.getAndIncrement () is as follows

public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } 

I cannot understand the purpose of writing a sequence of operations in an infinite loop. Does it use any special purpose in the Java Memory Model (JMM). Please help me find a descriptive understanding. Thanks in advance.

+7
source share
3 answers

I cannot understand the purpose of writing a sequence of operations inside an infinite loop of a loop.

To understand why this happens in an infinite loop, I find it useful to understand what compareAndSet does and how it can return false.

 Atomically sets the value to the given updated value if the current value == the expected value. Parameters: expect - the expected value update - the new value Returns: true if successful. False return indicates that the actual value was not equal to the expected value 

So, you read the Returns post and ask how is this possible?

If two threads call incrementAndGet at the same time, and both of them enter and see the value current == 1 . Both threads will create a thread-local next == 2 and try to set via compareAndSet . Only one thread will win as documented, and the thread that loses should retry.

This is how CAS works. You are trying to change the value, if you fail, try again, if you succeed, continue.

Now just declaring the field as volatile will not work, because the increment is not atomic. So, something kind of insecure from the script I explained

 volatile int count = 0; public int incrementAndGet(){ return ++count; //may return the same number more than once. } 
+4
source

I cannot understand the purpose of writing a sequence of operations in an infinite loop.

The purpose of this code is to ensure that the volatile field is updated accordingly without the overhead of locking synchronized . If there are not a large number of threads that compete for updating the same field, this will most likely start several times to accomplish this.

The volatile keyword provides memory visibility and synchronization, but does not in itself provide atomic operations with multiple operations (testing and installation). If you test and then set the volatile field, there are race conditions if several threads try to perform the same operation at the same time. In this case, if several threads try to increase the AtomicInteger value AtomicInteger same time, you can skip one of the increments. The parallel code here uses the spin loop and the compareAndSet base methods to ensure that the volatile int updated to 4 (for example) if it is still 3.

  • t1 gets an int atom and is 0.
  • t2 gets the int atom and is 0.
  • t1 adds 1 to it
  • t1 atomizes tests to make sure it is 0, it stores 1.
  • t2 adds 1 to it
  • t2 atomically checks to make sure that it is 0, it is not, so it must rotate and try again.
  • t2 gets the int atom and equals 1.
  • t2 adds 1 to it
  • t2 atomizes the tests to make sure it is 1, and stores 2.

Does it use any special purpose in the Java Memory Model (JMM).

No, it serves to define the class and method and uses JMM and language definitions around volatile to achieve its goal. JMM defines what the language does with synchronized , volatile and other keywords, and how several threads interact with cached and central memory. This is mainly about interacting with internal codes with the operating system and hardware, and rarely, if ever, about Java code.

This is the compareAndSet(...) method, which approaches JMM by calling the Unsafe class, which is mainly native methods with some shells:

 public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } 
+6
source

Java compareAndSet is based on CPU and CW comparison instructions (see http://en.wikipedia.org/wiki/Compare-and-swap . It compares the contents of a memory cell with a given value and only if they are the same, changes the contents of this cell memory to a given new value.

In the case of incrementAndGet, we read the current value and call compareAndSet(current, current + 1) . If it returns false, this means that another thread was interfering and changing the current value, which means that our attempt failed, and we need to repeat the entire cycle until this succeeds.

+1
source

All Articles