What is atomicity?

Atomicity means that an operation or operations will either succeed or fail, with no partial success or partial failure due to interruption.

Is i++ thread-safe?

Today we will start with this question to explain atomic operations in JAVA.

The i++ operation was known when we learned the basics of JAVA, but did you know it was thread-safe?

There must be someone in your heart, HMM… No, there is an answer!

Let’s do an experiment:

@Slf4j
public class Add {

    public static void main(String[] args) {
        AddThread addThread = new AddThread();
        // Create 10 threads
        IntStream.range(0.10).forEach(
                value -> new Thread(addThread).start()
        );
    }

    static class AddThread implements Runnable {
      	// Initializes the I value
        private int i = 0;

        @Override
        public void run(a) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Increment operation
            i++;
            log.info("Current thread :{}, I :{}", Thread.currentThread().getName(), i); }}}Copy the code

The result is as follows:

[thread-1] infocom.xiaozhi.simple.Add - Current Thread: thread-5, I :4 [thread-1] infocom.xiaozhi.simple.Add - Current Thread: thread-5 Thread-1, I :3 [thread-9] info.xiaozhi.simple.Add [thread-4] infocom.xiaozhi.simple.Add [thread-6] infocom.xiaozhi.simple.Add [thread-0] infocom.xiaozhi.simple.Add Thread-0, I :2 [thread-2] info.xiaozhi.simple.Add [thread-8] infocom.xiaozhi.simple.Add - current Thread: [thread-7] infocom.xiaozhi.simple.Add - Current Thread: Thread-7, I :6 [thread-3] info.xiaozhi.simple. Add - Current Thread: thread-3, I :3Copy the code

It can be seen that the I values of some threads are the same (for example, thread-1 and thread-3), which indicates that threads are unsafe. How to solve this problem?

I believe everyone must have their own solution, here will not post the detailed code, the code has been uploaded code cloud, at the end of the link.

  1. The core code uses the synchronized modifier
  2. The core code uses the display Lock Lock package
  3. Using the JDK’s AtomicInteger class

How about synchronized, Lock, and AtomicInteger?

Then we use the JMH measurement tool introduced in the last article to perform the performance test code is as follows:

@BenchmarkMode(Mode.AverageTime)  // Count the average time
@OutputTimeUnit(TimeUnit.MICROSECONDS) // Microseconds
@Warmup(iterations = 10) // Preheat 10 times
@Measurement(iterations = 10)  // Measure 10 times
public class SynchronizedVsLockVsAtomicInteger {

    @State(Scope.Group)
    public static class SynMonitor {
        private int i = 0;

        public void synInc(a) {
            synchronized (this) { i++; }}}@State(Scope.Group)
    public static class LockMonitor {
        private final Lock lock = new ReentrantLock();
        private int i = 0;

        public void lockInc(a) {
            lock.lock();
            try {
                i++;
            } finally{ lock.unlock(); }}}@State(Scope.Group)
    public static class AtomicIntegerMonitor {
        private AtomicInteger i = new AtomicInteger(0);

        public void inc(a) { i.incrementAndGet(); }}// Define benchmark methods to test the performance of synchronized modifiers
    @GroupThreads(10) // Number of threads
    @Group("sync") // The thread group name
    @Benchmark
    public void synInc(SynMonitor synMonitor) {
        synMonitor.synInc();
    }

    // Define benchmark methods to test display Lock performance
    @GroupThreads(10)
    @Group("lock")
    @Benchmark
    public void lockInc(LockMonitor lockMonitor) {
        lockMonitor.lockInc();
    }

  	// Define a benchmark method to test AtomicInteger performance
    @GroupThreads(10)
    @Group("atomic")
    @Benchmark
    public void atomicInc(AtomicIntegerMonitor atomicIntegerMonitor) {
        atomicIntegerMonitor.inc();
    }

    public static void main(String[] args) throws Exception {
        Options options = new OptionsBuilder().include(SynchronizedVsLockVsAtomicInteger.class.getSimpleName())
                .addProfiler(StackProfiler.class)
                .forks(1)
                .timeout(TimeValue.seconds(10))
                .build();
        newRunner(options).run(); }}Copy the code

The benchmark results are as follows:

Benchmark Mode Cnt Score Error Units SynchronizedVsLockVsAtomicInteger. Atomic avgt 10 0.150 + / - 0.007 us/op SynchronizedVsLockVsAtomicInteger. Lock avgt 10. 0.247 + / - 0.005 us/op SynchronizedVsLockVsAtomicInteger sync avgt 10, 1.128 Plus or minus 0.408 us/opCopy the code

It is not difficult to see the performance order: AtomicInteger > display Lock Lock > synchronized keyword

If you’re careful, you’ll notice that stackprofiler. class is configured.

Secondary result "com. Xiaozhi. Simple. SynchronizedVsLockVsAtomicInteger. Atomic: · stack" : 98.8% 1.2% RUNNABLE WAITING Secondary result "com. Xiaozhi. Simple. SynchronizedVsLockVsAtomicInteger. Lock: · stack" : 21.8% 78.2% WAITING RUNNABLE Secondary result "com. Xiaozhi. Simple. SynchronizedVsLockVsAtomicInteger. Sync: · stack" : 79.9% BLOCKED 19.3% RUNNABLE 0.8% WAITINGCopy the code

Observe the thread status statistics above:

The AtomicInteger thread was 98.8% RUNNABLE and not BLOCKED; The BLOCKED state of synchronized was up to 79.9%;

So why is AtomicInteger performing so well? Did you get it all at once?

Atomic Atomic classes

The above explanation so much, but also to show the strength of the protagonist today.

  1. Do you know the atomic types in the JDK?

    Here are a few that you might use during development:

    1. AtomicInteger
    2. AtomicLong
    3. AtomicBoolean
    4. AtomicReference
    5. AtomicStampedReference
    6. AtomicArray
    7. AtomicFieldUpdater
  2. How and in what scenarios are so many atomic types used?

    Today we are going to talk about AtomicInteger, AtomicLong in detail

AtomicInteger

Like int’s reference type Integer, it inherits from Number; But AtomicInteger also provides many atomic operations.

AtomicInteger has an internal member variable value that is modified by the volatile keyword. In fact, AtomicInteger provides methods that operate primarily on that variable value.

The following is an excerpt from the source code:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
  
    private static final long valueOffset;
  	//volatile modifies our value
    private volatile int value;
}
Copy the code
  1. Create AtomicInteger

    1Create AtomicInteger AtomicInteger =new AtomicInteger();
    
    2Create AtomicInteger AtomicInteger =new AtomicInteger(1234567);
    Copy the code

    In fact, a no-parameter construct is equivalent to a parameterized construct passing in a value of 0

  2. Incremental operation

    Operations such as I ++ or I = I + 1 are non-atomic, so we can use the AtomicInteger method of Incremental operations

    int getAndIncrement()

    Returns the current value and increments it

    @Test
    public void getAndIncrement(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 18 == ai.getAndIncrement();
      assert 19 == ai.get();
    }
    Copy the code

    int incrementAndGet()

    Returns the incremented value directly

    @Test
    public void incrementAndGet(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 19 == ai.incrementAndGet();
      assert 19 == ai.get();
    }
    Copy the code
  3. Decremental operation

    I — or I = i-1 is also non-atomic, so we can also take advantage of atomic Decremental in AtomicInteger

    int getAndDecrement()

    Returns the current value and decrement it

    @Test
    public void getAndDecrement(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 18 == ai.getAndDecrement();
      assert 17 == ai.get();
    }
    Copy the code

    int decrementAndGet()

    Returns the decrement value directly

    @Test
    public void decrementAndGet(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 17 == ai.decrementAndGet();
      assert 17 == ai.get();
    }
    Copy the code
  4. Atomically update value values

    boolean compareAndSet(int expect, int update)

    Expect represents the current AtomicInteger value, update represents the value to be set, and this method returns a Boolean result

    When the expect value is inconsistent with the current value, the modification fails and false is returned

    Note: Boolean weekCompareAndSet(int expect, int update) is the same as the current method

    @Test
    public void compareAndSet(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      
      // The incoming value is inconsistent with the ai value
      assert! ai.compareAndSet(123.24);
      assert 18 == ai.get();
    
      // The incoming value is consistent with the ai value
      assert ai.compareAndSet(18.24);
      assert 24 == ai.get();
    }
    Copy the code

    int getAndAdd(int delta)

    Return the current value directly, and then add the value to the delta

    This method is actually atomic operation based on spin +CAS algorithm

    @Test
    public void getAndAdd(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 18 == ai.getAndAdd(13);
      assert 31 == ai.get();
    }
    Copy the code

    int addAndGet(int delta);

    Add the value to the delta value and return it directly

    @Test
    public void addAndGet(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 31 == ai.addAndGet(13);
      assert 31 == ai.get();
    }
    Copy the code
  5. AtomicInteger and functional interfaces

    Functional interfaces were introduced in JDK1.8, and AtomicInteger also provides support for functional interfaces

    int getAndUpdate(IntUnaryOperator updateFunction)

    int updateAndGet(IntUnaryOperator updateFunction)

    @Test
    public void getAndUpdate02(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      // Pass in a lambda expression
      assert 18 == ai.getAndUpdate(i -> i * 3);
      assert 54 == ai.get();
    }
    
    @Test
    public void updateAndGet02(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      // Pass in a lambda expression
      assert 54 == ai.updateAndGet(i -> i * 3);
      assert 54 == ai.get();
    }
    Copy the code

    int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction)

    int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction)

    @Test
    public void getAndAccumulate(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      // Use lambda expressions
      assert 18 == ai.getAndAccumulate(14, Integer::sum);
      assert 32 == ai.get();
    }
    
    @Test
    public void accumulateAndGet(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      assert 35 == ai.accumulateAndGet(17, Integer::sum);
      assert 35 == ai.get();
    }
    Copy the code

    When AtomicInteger supports functional interfaces, we should be extremely happy that we can DIY our methods and implement them atomically

    Custom functional interfaces are as follows:

    @Test
    public void getAndAccumulate02(a) {
      final AtomicInteger ai = new AtomicInteger(18);
      
      assert 18 == ai.getAndAccumulate(14.new IntBinaryOperator() {
        @Override
        public int applyAsInt(int left, int right) {
          assert 18 == left;
          assert 14 == right;
    
          // Add the two values
          int temp = left + right;
          // The result is enlarged 7 times for return
          return temp * 7; }});// Get the value of the custom functional operation
      assert 224 == ai.get();
    }
    Copy the code
  6. AtomicInteger has two more methods

    void set(int value)

    void lazySet(int value)

    Both methods achieve the same effect, modifying the value of AtomicInteger directly

    The set method’s modification of a volatile value is forced to flush to main memory so that other threads can see it immediately, again as a memory barrier underlying the volatile keyword.

    Memory barriers, while lightweight enough, still incur performance overhead.

    When a set operation is performed in a single thread, there is no need to retain this memory barrier mechanism, so the lazySet method was born!

    JVM developers have taken performance to the extreme. Here admire, admire!

    For detailed results of the performance tests for both methods, see the cloud code link at the end of this article.

AtomicLong

Since the AtomicLong and AtomicInteger methods are basically the same, I won’t repeat them in this article.

Since the last article JMH issued, some friends said I was too long!

I was a little confused, and it turned out that my article was a little long; Ah, there is no way, in order to explain knowledge in detail, how can you take a quick look at the hasty thing?

But it’s too long and you can’t stand it, so let’s simplify it a little bit.

insider

After the long process above, we learned the use of AtomicInteger.

Now let’s take a look at the inside story of AtomicInteger!

When I first introduced AtomicInteger, I posted a snippet of the source code. Let’s look at it again:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
Copy the code

Unsafe was written in C++, with lots of assembly CPU instruction code inside

ValueOffset Memory offset for storing a value

Let’s look at the source of the compareAndSet method – CAS

public final boolean compareAndSet(int expect, int update) {
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Copy the code

**unsafe.compareAndSwapInt(this, valueOffset, expect, update); ** is a native method, which can ensure that our operation is atomic. It will compare the incoming value with the current value. When the expect value is inconsistent with the current value, the modification will fail.

AddAndGet and other methods are also native operations, but they do not return failed operations. While the result of the spin.

AtomicLong underlying also have unsafe.com pareAndSwapIntLong method

Since native method is relatively low-level and decompiling is troublesome, MY knowledge is limited, so I will not go further!

Today AtomicInteger, AtomicLong learning here, I believe that we are relatively clear about this knowledge; I will update the other atomic classes in a later article.

Public Portal — AtomicInteger for Concurrent Programming

The code cloud code link is as follows:

Gitee.com/songyanzhi/…

Thanks for reading.

I wish you all a happy work and good health!