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.