Say what I said before

As I said at the beginning, we are going to put together some learning documents for Concurrent programming in Java, and this is the first one: atomic Operations. It mainly describes what atomic operation is, how to implement atomic operation and atomic operation classes in Java.

Open the bottle and fill it up

What is atomic operation

What is atomic operation? Atomic operation is an operation that cannot be interrupted. HMM… Specifically, it should be an operation that is not affected by other threads or tasks.

That’s right, atomic operation is the one time you go to the bathroom at home >> Go to the bathroom, lock it, and perform the operation….. Happy body and mind, unlock, leave…..

In the program, a thread will not be robbed of resources by other threads or tasks when performing a task, until the end of the task to release resources, other threads or tasks can use this resource.

HMM… Lock a resource. Lock a resource. Lock a resource.

Look at the case

We all know that I ++ is adding 1 to the I variable. Is i++ an atomic operation?

Take a look at the program:

Let’s start with the program: the static member variable X is the resource to operate on. The static method incr() increments x by one. Define the CountDownLatch object in the main method to ensure that x’s results are not output until all threads have finished. The loop creates 100 threads, each calling the incr() method 1000 times, and in theory the final value of x should be 100000.

package com.qidian.atom; import java.util.concurrent.CountDownLatch; /** * @author */ public class Test {private static int x = 0; Public static void main(String[] args) throws InterruptedException {// Define CountDownLatch before outputting the value of x CountDownLatch  cd = new CountDownLatch(100); For (int I = 0; i<100; I++){new Thread(" Thread A"){public void run() { j<1000; j++){ incr(); } cd.countDown(); } }.start(); } // Make sure all threads are finished before continuing cd.await(); System.out.println(x); system.out.println (x); Public static void incr(){x ++; }}Copy the code

If we do it a few times, we’ll see that sometimes x is not 100000.

And that’s because x plus plus is not an atomic operation. This means that you leave the door unlocked while you go to the bathroom, leading to the possibility of someone interrupting you in the middle of your journey. Of all the 100,000 times you have gone to the bathroom, there will always be a few unsuccessful attempts at relaxation…

Look at the picture to understand the interruption:

The first thing to know about x++ is that the actual operation has two steps:

Int z = x + 1;

Step 2: x = z;

Note: thread A and thread B both perform ++ operations on X.

Thread A takes the value of x and executes z = x +1, thread A takes A nap, thread B takes the value of x, executes z = x +1, and writes the value of z to the x variable. At this point x is changed from 10 to 11. Thread A wakes up and writes its calculated value of z (or 11) directly to the x variable. That will change the value of x from 11 to 11. At this point we see that thread B’s increment operation is overwritten. This causes thread B’s +1 operation to fail.

This is why the above program produces a non-100000 result. The solution is to lock the INCR method. While any thread operates on X, the other threads must wait. Even if the currently operating thread is asleep, other threads cannot manipulate X.

Well! Yes, this is to remind you that you must remember to lock the door…… Can never forget once happened in the university dormitory early morning toilet horror incident…..

As a result, a program can use synchronized to modify incR methods, and of course there are many ways to lock. We’ll talk more about locking in a later article.

package com.qidian.atom; import java.util.concurrent.CountDownLatch; Public class Test {private static int x = 0; private static int x = 0; Public static void main(String[] args) throws InterruptedException {// Define CountDownLatch before outputting the value of x CountDownLatch  cd = new CountDownLatch(100); For (int I = 0; i<100; I++){new Thread(" Thread A"){public void run() { j<1000; j++){ incr(); } cd.countDown(); } }.start(); } // Make sure all threads are finished before continuing cd.await(); System.out.println(x); system.out.println (x); Public synchronized static void incr(){x ++;} public synchronized static void incr(){x ++; }}Copy the code

After locking, the operation i++ in the incr method can be understood as an atomic operation. Of course, we also need to understand that any program as long as the lock will be efficient problems.

Atomic manipulation classes in Java

Java provides us with several atomic manipulation classes that provide useful apis. These apis operate on resources atomically, with no thread-safety issues. Let’s see see…

Yes is Java. Util. Concurrent. Atomic package all the classes.

Let’s look at some classic classes in action!

AtomicInteger

This class is an atomic operation class of type int.

Check out his AIP:

Construction method:

  • AtomicInteger() creates a new AtomicInteger with an initial value of 0.
  • AtomicInteger(int initialValue) creates a new AtomicInteger with the given initialValue.

Let’s look at some classic apis

  • AddAndGet (int delta) adds the given value atomically to the current value. And returns the updated int value.
  • CompareAndSet (int expect, int update) If the current value == is expected, the atom of that value is set to the given update value. Return true if the update was successful, false otherwise.
  • DecrementAndGet () atom decrements the current value by 1. And returns the updated value.
  • Get () gets the current value.
  • GetAndAdd (int delta) adds the given value atomically to the current value. And returns the value before the update.
  • GetAndDecrement () Atom minus 1 current value. And returns the value before the update.
  • GetAndIncrement () adds a current value to the atom. And returns the value before the update.
  • GetAndSet (int newValue) sets the atom to the given value and returns the old value.
  • IncrementAndGet () increments atoms by 1. And returns the updated value.
  • Set (int newValue) Sets the given value.

Come to! Start by understanding the API in terms of the program. Later we will look through the source code to see if there is any problem…

package com.qidian.atom; import java.util.concurrent.atomic.AtomicInteger; Public class Test1 {public static void main(String[] args) {AtomicInteger atomi = new AtomicInteger(10); // Add the given value atomically to the current value. int i = atomi.addAndGet(10); System.out.println(i); // 20 // If the current value == is the expected value, then the value atom is set to the given updated value. Return true if the update was successful, false otherwise. boolean b = atomi.compareAndSet(20, 30); System.out.println(b); // true System.out.println(atomi.get()); // atom minus 1 current value. And returns the updated value. i = atomi.decrementAndGet(); System.out.println(i); // 29 System.out.println(atomi.get()); // 29 // Adds the given value atomically to the current value. And returns the value before the update. i = atomi.getAndAdd(50); System.out.println(i); // 29 System.out.println(atomi.get()); // 79 // atom minus 1. And returns the value before the update. i = atomi.getAndDecrement(); System.out.println(i); // 79 System.out.println(atomi.get()); // add a current value to the atom. And returns the value before the update. i = atomi.getAndIncrement(); System.out.println(i); // 78 System.out.println(atomi.get()); // 79 // Sets the atom to the given value and returns the old value. i = atomi.getAndSet(100); System.out.println(i); // 79 System.out.println(atomi.get()); // 100 // Add a current value to the atom. And returns the updated value. i = atomi.incrementAndGet(); System.out.println(i); // 101 System.out.println(atomi.get()); / / 101}}Copy the code

OK! Look at the source code:

Well… AtomicInteger addAndGet AtomicInteger addAndGet AtomicInteger

 /**
  * Atomically adds the given value to the current value.
  *
  * @param delta the value to add
  * @return the updated value
  */
 public final int addAndGet(int delta) {
     return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
 }
Copy the code

The unsafe class is the first to use the broadening method.

public final int getAndAddInt(Object var1, long var2, int var4) { int var5; Do {// volatile um..... Var5 = this.getIntVolatile(var1, var2); var5 = this.getIntVolatile(var1, var2); // The unsafe compareAndSwapInt is the final substitution for data. } while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }Copy the code

MMM MMM MMM! Let’s look at some other methods:

CompareAndSet method

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

DecrementAndGet method source:

 public final int decrementAndGet() {
     return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
 }
Copy the code

GetAndAdd method source:

 public final int getAndAdd(int delta) {
     return unsafe.getAndAddInt(this, valueOffset, delta);
 }
Copy the code

That’s pretty much it. The UnsafeInteger CAS is the underlying implementation of all apis in the AtomicInteger class that do atomic operations to update values.

So what’s wrong with Unsafe’s CAS? (There will be a detailed explanation about CAS in the following article. Welcome to pay attention. You know, I love it when you pay attention to me. (^__^) Hee hee…

Of course, all see here, on the point of saving it!

AtomicReference

Take a look at the description of this class:

Public Class AtomicReference<V> extends Object implements Serializable Object reference that can be updated atomically.Copy the code

In plain English, atomic operation classes for any reference type.

Serve straight:

Public class Test3 {public static void main(String[] args) {public static void main(String[] args) { AtomicReference<String> ar = new AtomicReference<>(" kakashi "); System.out.println(ar.get()); // Kakashi String s = ar.get(); Boolean b = ar.compareAndSet(s, "qilu "); System.out.println(b); // true System.out.println(ar.get()); }}Copy the code

Well… It should be easy to understand!

CAS has a small problem with ABA. Take a 5-minute break……… Check out my other posts at……

ABA problem

Come on, let me talk a little bit about ABA.

CAS: CAS: Compare And Swap. What does that mean? Look at a map

There is a chicken in a container. Xiao Ming has a chicken in his left hand and a duck in his right hand. The left handed chicken is the expected value of the container. If the left handed chicken is the same as the container chicken, the right handed duckling is used to replace the container chicken. If the expected value of the left hand is different from that of the chicken in the container, forget it.

OK, look at the ABA question:

It’s a mess. Take a look at our description:

Xiao Ming is going to replace a chicken with a duck, so he checks the contents of the container and finds that it is a chicken. He picks up a chicken in his right hand as the expected value. The CAS is ready. But at this time, Xiao Ming has a stomachache and goes to the toilet. Such as flower began….

②③ The blue flower uses CAS to replace the chicken in the container with a pig.

④⑤ Flower do not know why feel chicken is not cost-effective, so use another chicken to change back the pig.

At this time, Xiao Ming went to the toilet, so he picked up the chicken he had prepared before and compared it with the chicken in the container. He found it was the same, so he used a duck to replace the chicken in the container.

In this process, the problem was that Ming prepared a chicken according to the contents of the container at the beginning, and when he came back to compare the chicken after going to the toilet, the chicken in the container had been replaced like flowers, which was not the same chicken as before. One is not the other. Therefore, xiao Ming’s CAS should not succeed in theory, but the result is the lack of success, which is the classic ABA problem.

Look at the program to understand

package com.qidian.atom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; Private static AtomicInteger ai = new AtomicInteger(3); private static AtomicInteger ai = new AtomicInteger(3); Public static void main(String[] args) {public void run() {public void run() {public void run() {int I = ai.get(); / / this value as the expected value / / a nap try {TimeUnit. MICROSECONDS. Sleep (50); } catch (InterruptedException e) { e.printStackTrace(); } // use cas to replace Boolean b = ai.compareAndSet(I, 4); System.out.println(" thread 1 replaces 3 with 4: "+b); } }.start(); New Thread(){public void run() {int I = ai.get(); Boolean b = ai.compareAndSet(I, 4); System.out.println(" thread 2 replaces 3 with 4: "+b); B = ai.compareAndSet(4, 3); System.out.println(" thread 2 replaces 4 with 3: "+b); } }.start(); }}Copy the code

Results:

In theory, the 3 that was pulled out before thread 1 is no longer the 3 that it is now, but it’s still done.

Atomic manipulation classes also have ABA problems, so a new solution is prepared, the AtomicStampedReference class.

AtomicStampedReference

Take a look at the description of this class:

Public Class AtomicStampedReference<V> extends Object An AtomicStampedReference maintains an Object reference and integer stamp that can be updated atomically.Copy the code

Key word: “imprinting”. What does that mean?

This class solves the ABA problem by adding a “stamp” to each value, just as Ming would put a stamp on his chicken and Ruhua would put a stamp on her chicken, so that we can easily determine whether the chicken is the other chicken by using the stamp.

Look at the constructor:

AtomicStampedReference(V initialRef, int initialStamp) Creates a new AtomicStampedReference with the given initial value.

This class has a limited API, but there are two classic methods:

  • Boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) sets the values of the reference and the given update value of the stamp atomically, If the current reference is == to the expected reference and the current flag is equal to the expected flag.
  • V getReference() Returns the current value of the reference.
  • Int getStamp() returns the current value of the stamp.
  • Void set(V newReference, int newStamp) sets the value of the reference and stamp unconditionally.

Look at the program to understand

package com.qidian.atom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicStampedReference; Public class Test4 {private static AtomicStampedReference<String> asr = new AtomicStampedReference<>(" Kakashi ",1); Public static void main(String[] args) {// Thread 1 new Thread(){public void run() {// Value String s = asr.getreference (); Int stamp = asr.getstamp (); / / a nap try {TimeUnit. MICROSECONDS. Sleep (500); } catch (InterruptedException e) { e.printStackTrace(); } // Replace values Parameter Description (original value, new value, original stamp, new stamp) Boolean b = asr.compareAndSet(" kakasi "," qimu kakasi ",stamp,stamp+1); System.out.println(" thread 1, result of substitution: "+b); } }.start(); Thread 1 new Thread(){public void run() {// Value String s = asr.getreference (); // Thread 1 new Thread(){public void run() {// Value String s = asr.getreference (); Int stamp = asr.getstamp (); Boolean b = asr.compareAndSet(" qakasi "," qakasi ",stamp,stamp+1); System.out.println(" thread 2, result of first substitution: "+b); B = asr.compareAndSet(" flag wooden 50/50 "," kakashi ",stamp,stamp+1); System.out.println(" thread 2, result of second substitution: "+b); } }.start(); }}Copy the code

Results:

A little clarification:

Because every time in the COMPARISON of CAS, not only the expected value should be compared, but also the “stamp”. And every time you replace it, you have to not only change the value, but also change the imprint, so even if the value is the same, if the imprint is different, the replacement will still fail.

Thread 1 has failed to go to CAS after napped. Thread 1 has failed to go to CAS after napped. Thread 1 has failed to go to CAS.

Other atomic classes

As for the other atomic classes!!!! Well!!!!!! Pretty much the same, just a few more types, like AtomicLong, AtomicIntegerArray, whatever. I won’t repeat it.

There are some that don’t work, I won’t go into details, but if you’re interested, you can study them, and we’ll talk about them.

That’s all for this one. Welcome to follow, remember to like…