preface

We know that an i++ operation is actually thread-unsafe because an i++ operation has three steps:

  • 1. Get the value of I
  • 2. Run I +1
  • Assign the result of I +1 to I

However, these three steps are not an atomic operation, so thread insecurity will occur in multi-threaded environment.

Java since JDK 1.5, in Java. Util. Concurrent. The atomic package provides 12 corresponding atomic classes under operation, that we can directly use atomic operations class to implement an atom i++ operation.

Java provides a total of 12 atomic class operations, which can be divided into four types:

  • Atomic update base type
  • Atomic update array
  • Atomic update reference
  • Atomic update attribute

Let’s take a look at the 12 atomic manipulation classes in each of these four categories:

Atomic update base type

There are three basic data types for atomic update:

  • AtomicInteger: Atom update integer.
  • AtomicBoolean: Atom updates Boolean type.
  • AtomicLong: Atom updates long integers.

AtomicInteger

The common methods are as follows:

  • Int addAndGet(int delta) : Atomically adds the passed value to the value in the instance (the value in AtomicInteger) and returns the result.
  • Boolean compareAndSet(int expect, int update) : If the value entered is equal to the expected value, set it atomically to the entered value, returning true on success, false on failure
  • Int getAndIncrement() : atomically increments the current value by 1 and returns the previous value.
  • Int getAndDecrement() : Atomically decreases the current value by 1 and returns the pre-reduction value.
  • Void lazySet(int newValue) : this method will eventually be set to newValue. Note that this method is lazy, which means that the value is not immediately updated to main memory, so other threads may not see the value for a while.
  • Int getAndSet(int newValue) : Atomically sets the value to newValue and returns the old value.

Code sample

package com.zwx.concurrent.atomic; import java.util.concurrent.atomic.AtomicInteger; public class TestAtomicBasicData { public static void main(String[] args) throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(8); System.out.println(" initialize :" + atomicInteger); / / 8 atomicInteger.com pareAndSet (8, 10); System.out.println(" after CAS :" + atomicInteger); //10 System.out.println(atomicInteger.getAndIncrement()); System.out.println(" after autoincrement: "+ atomicInteger); //11 System.out.println(atomicInteger.getAndDecrement()); //11 system.out. println(" after decrement: "+ atomicInteger); / / 10}}Copy the code

AtomicBoolean

To update a Boolean value, do the following:

  • Boolean compareAndSet(Boolean expect, Boolean update) :

    If the value entered is equal to the expected value, it is set atomically to the value entered, returning true on success and false on failure. Note that Boolean is actually converted to int first, 0- no 1- yes

  • Void lazySet(Boolan newValue) will eventually be set to newValue. Note that this method is lazy, which means that the value is not immediately updated to main memory, so other threads may not see the value for a short period of time.
  • Boolean getAndSet (Boolean newValue) :

    Atomically set the value to newValue,And returns the old value.

AtomicLong

This is almost the same as the AtomicInteger above, so I won’t give you an example.

Atomic operations are all implemented using CAS operations in the Unsafe class, but only three types of CAS operations are available in Unsafe:



So the Boolean type above is converted to integer for CAS, other data types can also be converted to atomic operation through CAS.

Atomic update array

Atomic operation update arrays also provide three types:

  • AtomicIntegerArray: Atoms are elements in an array of type Integer.
  • AtomicLongArray: Atom updates elements in an array of type Long.
  • AtomicReferenceArray: The atom updates the element in the array of reference types.

AtomicIntegerArray

Int [] arr = new int[]{1,2,3}; AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);Copy the code



And you can see that when you initialize it, you make a copy of the array, so it’s not going to affect the value of the original array if you change the value.

Boolean compareAndSet(int I, int expect, int update) : Boolean compareAndSet(int I, int expect, int update) : Boolean compareAndSet(int I, int expect, int update) : Boolean compareAndSet(int I, int expect, int update) : Boolean compareAndSet(int I, int expect, int update) : Boolean compareAndSet(int I, int expect, int update) :

Code sample

package com.zwx.concurrent.atomic; import java.util.concurrent.atomic.AtomicIntegerArray; Public class TestAtomicArray {public static void main(String[] args) {int[] arr = new int[]{1,2,3}; AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr); AtomicIntegerArray.com pareAndSet,2,8 (1); System.out.println(arr[1]); Println (atomicIntegerArray.get(1)); //atomicIntegerArray is changed to 8}}Copy the code

AtomicLongArray

The method is the same as the base AtomicLong, with an index subscript.

AtomicReferenceArray

This method is the same, the only difference being that you can pass in a generic, that is, an element in the data as a custom object rather than a reference object.

Code examples:

package com.zwx.concurrent.atomic; import com.alibaba.fastjson.JSONObject; import java.util.concurrent.atomic.AtomicReferenceArray; Public class TestAtomicReferenceArray {public static void main(String[] args) {Man Man = new Man(18," 三"); Man[] arr = new Man[]{man}; AtomicReferenceArray<Man> atomicReferenceArray = new AtomicReferenceArray<>(arr); Println (" before: "+ jsonObject.tojsonString (atomicReferencearray.get (0))); / / {" age ": 18," name ":" * * "} Man updateMan = new Man (28, "bill"); atomicReferenceArray.compareAndSet(0,man,updateMan); Println (" before: "+ jsonObject.tojsonString (atomicReferencearray.get (0))); //{"age":28,"name":" l "}}} class Man{protected Integer age; private String name; public Man(Integer age, String name) { this.age = age; this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Copy the code

Atomic update reference types

Atomic updates of base types can only update one variable at a time. What if we need multiple atomic updates? At this point we can combine multiple variables into one, so we need to update the class provided by the reference type using the atom.

Atomic update reference types also provide three classes:

  • AtomicReference: Atomic update reference type.
  • AtomicMarkableReference: Atom updates reference types with marker bits. You can atomically update a Boolean tag bit and reference type.
  • AtomicStampedReference: Atom updates a reference type with a version number. This class associates integer values with references and can be used for atomic update data and the version number of the data, which can solve ABA problems that may occur when atomic updates are made using CAS.

AtomicReference

To construct a reference object and then call the relevant atomic method in the AtomicReference, let’s look at a code example:

Code sample

package com.zwx.concurrent.atomic; import com.alibaba.fastjson.JSONObject; import java.util.concurrent.atomic.AtomicReference; Public class TestAtomicReference {public static void main(String[] args) {User oldUser = new User(18," 三"); AtomicReference<User> atomicReference = new AtomicReference<>(oldUser); System.out.println("CAS: "+ jsonObject.tojsonString (atomicReference.get())); / / {" age ": 18," name ":" * * "} User upateUser = new User (28, "bill"); boolean result =atomicReference.compareAndSet(oldUser,upateUser); System.out.println("CAS result: "+ result); //true system.out.println ("CAS: "+ jsonObject.tojsonString (atomicReference.get()))); //{"age":28,"name":" I "}}} class User{volatile Integer age; private String name; public User(Integer age, String name) { this.age = age; this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Copy the code

The same principle applies here, using Uasafe’s CAS operation Object to implement atomic operations:

AtomicMarkableReference

This is basically the same as the above AtomicReference, the only difference is that there is a mark, Boolean type.

The sample

package com.zwx.concurrent.atomic; import com.alibaba.fastjson.JSONObject; import java.util.concurrent.atomic.AtomicMarkableReference; Public class TestAtomicReferenceMark {public static void main(String[] args) {Person = new Person(18," 三"); AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(person,false); System. The out. Println (" ever marked: "+ atomicMarkableReference. IsMarked ()); System. The out. Println (" CAS before: "+ JSONObject. ToJSONString (atomicMarkableReference. GetReference ())); //{"age":18,"name":" @updatePerson "} Person = new Person(28," @updatePerson "); /** * arg1: indicates the expected reference object * arg2: indicates the reference object to be updated * arg3: indicates the expected tag * arg4: A mark of update * need parameters 1 and 3 are expected value will CAS success * / atomicMarkableReference.com pareAndSet (person, updatePerson, false, true); System. The out. Println (" after the CAS: "+ JSONObject. ToJSONString (atomicMarkableReference. GetReference ())); //{"age":28,"name":" I "}}} class Person{private Integer age; private String name; public Person(Integer age, String name) { this.age = age; this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Copy the code

AtomicMarkableReference principle analysis



Initializing an object requires initializing a reference object and an initial mark, which in turn are managed by its static inner class Pair:

AtomicStampedReference

This is exactly the same as AtomicMarkableReference, the only difference is that the tag in AtomicMarkableReference is only true and false, and the tag in AtomicStampedReference is an int, Can be regarded as version number, can solve CAS ABA problem.

Atomic update attribute

  • AtomicIntegerFieldUpdater: atomic updates integer field updater.
  • AtomicLongFieldUpdater: A updater that atomically updates long integer fields.
  • AtomicReferenceFieldUpdater: atomic updates any specified field reference types.

AtomicIntegerFieldUpdater

This is used to update a property of type int in a reference object, modifying the property using reflection. The following points need to be noted:

  • The attribute in the reference type must be int, not the wrapper class Integer
  • Attributes in reference types must be volatile
  • Attributes in reference types cannot be modified by private

Code sample

package com.zwx.concurrent.atomic; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class TestAtomicReferenceField { public static void main(String[] args) { //AtomicIntegerFieldUpdater Women women = new Women(18," zhang SAN "); //arg1: the referenced object type arg2: To modify the attributes of the object AtomicIntegerFieldUpdater AtomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Women.class,"age"); AtomicIntegerFieldUpdater.com pareAndSet (" women, 18, 28); System.out.println("CAS: "+ women.getage ()); //28 } } class Women{ volatile int age; private String name; public Women(int age, String name) { this.age = age; this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Copy the code

NewUpdater method:



AtomicIntegerFieldUpdaterImpl initialization is used in the reflective access to modify the attribute, and some verification:

AtomicLongFieldUpdater

This is the same as the AtomicLongFieldUpdater, which is used to update properties of type long with the following points:

  • The attribute in the reference type must be long, not the wrapper class long
  • Attributes in reference types must be volatile
  • Attributes in reference types cannot be modified by private

AtomicReferenceFieldUpdater

While the above two can only update the specified data type, this can update any specified type of property. There are also the following points to note:

  • An attribute in a reference type cannot be a primitive datatype and must be wrapped with a corresponding class (as opposed to the above two).
  • Attributes in reference types must be volatile
  • Attributes in reference types cannot be modified by private

The sample

package com.zwx.concurrent.atomic; import com.alibaba.fastjson.JSONObject; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; Public class TestAtomicReferenceField {public static void main(String[] args) {/** * arg2: Pass in the attribute type of the reference object * arg3: Introduced to modify the property name * / AtomicReferenceFieldUpdater atomicReferenceFieldUpdater1 = AtomicReferenceFieldUpdater.newUpdater(Women.class,Integer.class,"age"); AtomicReferenceFieldUpdater atomicReferenceFieldUpdater2 = AtomicReferenceFieldUpdater.newUpdater(Women.class,String.class,"name"); Women Women = new Women(18," zhang SAN "); AtomicReferenceFieldUpdater1.com pareAndSet (" women, 18, 28); AtomicReferenceFieldUpdater2.com pareAndSet (" women, "zhang", "li si"); System.out.println(JSONObject.toJSONString(women)); //{"age":28,"name":" r "}}} class Women{volatile Integer age; volatile String name; public Women(int age, String name) { this.age = age; this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Copy the code

ABA problem in CAS operation

As mentioned in the previous article about AQS synchronization queues, ABA problems can be avoided by introducing a version number to spell the actual value together. In fact, we can also use AtomicMarkableReference and AtomicStampedReference to implement ABA problems. However, AtomicMarkableReference has only true and false tags, and you can select an AtomicStampedReference based on your service requirements.

conclusion

In this article, we introduce 12 types of atomic manipulation classes in Java, all of which are implemented through CAS methods in the Unsafe class. The unsafe-looking CAS method is a type of atomic manipulation with tags and version numbers to avoid ABA problems.

In the next article, we’ll look at how thread pools are implemented.