When A program updates A variable, if multiple threads update the variable at the same time, it may get unexpected values, such as i=1, thread A updates i+1, and thread B updates i+1. After two thread operations, i may not be equal to 3, but equal to 2 Because threads A and B get 1 when updating variable i, which is an unsafe update operation for threads. We usually use synchronized to solve this problem. Synchronized will ensure that multiple threads will not update variable i at the same time.
And Java from jdk1 5 began to provide Java util. concurrent. Atomic package (hereinafter referred to as atomic package). The atomic operation class in this package provides a simple, efficient and thread safe way to update a variable.
Because there are many types of variables, the Atomic package provides a total of 13 classes, which belong to four types of Atomic update methods: Atomic update basic type, Atomic update array, Atomic update reference and Atomic update attribute (field). The classes in the Atomic package are basically wrapper classes implemented by Unsafe.
1. Atomic update basic type class
Update basic types in an Atomic way. The Atomic package provides the following three classes
(1) AtomicBoolean: atomic update boolean type.
(2) AtomicInteger: atomic update type.
(3) AtomicLong: atomic update long type.
Commonly used methods of AtomicInteger are as follows:
a. int addAndGet(int delta): atomically add the entered value to the value in the instance (value in AtomicInteger) and return the result.
b. boolean compareAndSet(int expect, int update): if the entered value is equal to the expected value, the value will be set to the entered value atomically.
c. int getAndIncrement(): add 1 to the current value in an atomic way. Note that the value before self increment is returned here.
d. void lazySet(int newValue): it will eventually be set to newValue. After using lazySet to set the value, other threads may still be able to read the old value in a short period of time.
e. int getAndSet(int newValue): the value set atomically to newValue and returns the old value.
AtomicInteger sample code:
public class AtomicIntegerTest { static AtomicInteger ai = new AtomicInteger(1); public static void main(String[] args){ System.out.println("Log information of magic leather:---->"+ ai.getAndIncrement()); System.out.println("Log information of magic leather:---->"+ ai.get()); } }
So how does getAndIncrement implement atomic operations? Let's analyze the implementation principle and the source code of getAndIncrement:
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
The Atomic package provides three basic types of Atomic updates, but the basic types of Java include char, float and double. So the question is, how to update other types of atoms? Classes in Atomic package are basically implemented with Unsafe. Let's take a look at the source code of Unsafe:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
2. Atomic update array
Update an element in the array in an Atomic way. The Atomic package provides the following four classes:
a. AtomicIntegerArray: atom updates elements in an integer array.
b. AtomicLongArray: atom updates the elements in a long array.
c. AtomicReferenceArray: atomic updates elements in an array of reference types.
AtomicIntegerArray class mainly provides the atomic way to update the integer in the array. Its common methods are as follows:
int addAndGet(int i,int dalta): atomically add the input value to the element of index I in the array.
boolean compareAndSet(int i,int expect,int update): if the current value is equal to the expected value, set the element at array position I to the update value atomically.
The usage example code of AtomicIntegerArray is as follows:
public class AtomicIntegerArrayTest { static int[] value = new int[] {1,2}; static AtomicIntegerArray ai = new AtomicIntegerArray(value); public static void main(String[] args){ ai.getAndSet(0,3); System.out.println(ai.get(0)); System.out.println(value[0]); } }
It should be noted that the array value is passed out through the construction method, and then AtomicIntegerArray will copy the current array. Therefore, when AtomicIntegerArray modifies the internal array elements, the incoming array will not be affected.
3. Atomic update reference type
AtomicInteger of Atomic update basic type can only update one variable. If the atom updates multiple variables, you need to use this atom to update the class provided by the reference type. The Atomic package provides the following three classes.
AtomicReference: atomic update reference type.
AtomicReferenceFieldUpdater: updates fields in atomic reference types.
AtomicMarkableReference: atomic updates reference types with marker bits. You can update a Boolean tag and reference type atomically.
The construction method is atomicmarkablereference (V initialref, Boolean initialmark).
The methods provided by the above classes are almost the same.
public class AtomicReferenceTest { public static AtomicReference<User> atomicRef = new AtomicReference<User>(); public static void main(String[] args){ User user = new User("conan",15); atomicRef.set(user); User updateUser = new User("Shinichi",17); atomicRef.compareAndSet(user,updateUser); System.out.println(atomicRef.get().getName()); System.out.println(atomicRef.get().getOld()); } static class User{ private String name; private int old; public User(String name,int old){ this.name=name; this.old=old; } public String getName() { return name; } public int getOld() { return old; } } }
First, a user object is built in the code, then the user object is set in AtomicReference, and finally compareAndSet method is used to update the atom, and the compareAndSet principle is implemented in AtomicInteger.
4. Atomic update field class
If you need to update a field in a class atomically, you need to use the Atomic update field class. The Atomic package provides the following three classes for Atomic field update.
(1) AtomicIntegerFieldUpdater: the updater that updates the field of an atomic integer.
(2) AtomicLongFieldUpdater: the updater that updates long integer fields.
(3) AtomicStampedReference: atomic updates the reference type with version number.
This class associates integer values with references, which can be used for atomic update data and data version number, and can solve the ABA problem that may occur when using CAS for atomic update.
It takes two steps to update the field class atomically.
Step 1: because the atomic update field classes are abstract classes, you must use the static method newUpdater() to create an updater every time you use it, and you need to set the classes and properties you want to update.
Step 2: the public volatile modifier must be used to update the fields (properties) of the class.
public class AtomicIntegerFieldUpdaterTest { // Create an atomic updater and set the object class and object properties that need to be updated private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class,"old"); public static void main(String[] args){ // Set Conan's age to 10 User conan = new User("conan",10); // Conan is one year older, but he will still output his old age System.out.println(a.getAndIncrement(conan)); // Output Conan's current age System.out.println(a.get(conan)); } public static class User{ private String name; public volatile int old; public User(String name,int old){ this.name=name; this.old=old; } public String getName() { return name; } public int getOld() { return old; } } }
This chapter introduces the 13 atomic operation classes in JDK and the implementation principle of atomic operation classes. You need to be familiar with the use scenarios of classes and use them in appropriate occasions.