Description of the number of atomic classes
Before JDK7 included 7, there were 12 java atomic classes, as shown below
Four atomic operation classes appear in JDK8, as shown in the following picture
Atomic update base type class
Update basic types in an Atomic way. The Atomic package provides the following three classes.
- AtomicBoolean: Atomic update boolean type.
- AtomicInteger: Atomic update integer.
- AtomicLong: Atomic update long integer.
The above 3 categories as like as two peas are provided. The AtomicInteger is used as an example to explain the details. AtomicIngeter's common methods are as follows:
- int addAndGet(int delta): adds the entered value to the value in the instance in an atomic manner and returns the result.
- boolean compareAndSet(int expect, int update): if the current value is equal to the expected value, set the current value to the updated value atomically.
- int getAndIncrement(): add 1 to the current value in an atomic way. Note that the value returned here is the value before self increment, that is, the old value.
- void lazySet(int newValue): it will eventually be set to newValue. After setting the value with lazySet, other threads may still be able to read the old value in a short period of time.
- int getAndSet(int newValue): set to newValue atomically and return the old value.
Code example
static AtomicInteger ai =new AtomicInteger(1); public static void main(String[] args) { System.out.println(ai.getAndIncrement()); System.out.println(ai.get()); }
Output results
1
2
Let's take a look at how getAndIncrement() implements atomic operations
public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
Code parsing
We get the old value, then pass the data to be added, and call getAndAddInt() to update the atom. In fact, the core method is compareAndSwapInt(), which uses CAS to update. We only provide 3 CAS operations in Unsafe,
In addition, note that AtomicBoolean converts a Boolean to an integer and operates using compareAndSwapInt.
/** * If the current value is var4, the atomic java variable is updated to var5 or var6 * @return Returns true if the update is successful */ 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);
Atomic update array
Update an element in the array in an Atomic way. The Atomic package provides the following three classes:
- AtomicIntegerArray: Atom updates the elements in an integer array.
- AtomicLongArray: Atom updates the elements in a long array.
- AtomicReferenceArray: Atom updates the elements in the reference type array.
The most common methods of these three classes are the following two methods:
- get(int index): get the element value with index.
- 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 following is an example of AtomicReferenceArray
static int[] value =new int[]{1,2}; static AtomicIntegerArray ai =new AtomicIntegerArray(value); public static void main(String[] args) { ai.compareAndSet(0,1,5); System.out.println(ai.get(0)); System.out.println(value[0]); }
Output results
5
1
It should be noted that the array value is passed in through the constructor method, and then AtomicIntegerArray will copy the current array. Therefore, when AtomicIntegerArray modifies the internal array elements, the passed in array will not be affected
public AtomicIntegerArray(int[] array) { // Visibility guaranteed by final field guarantees this.array = array.clone(); }
Atomic update reference type
AtomicInteger of Atomic update basic type can only update one value. If you update multiple values, such as the value in an object, you need to update the class provided by the reference type with atom. The Atomic package provides the following three classes:
- AtomicReference: Atomic update reference type.
- AtomicReferenceFieldUpdater: Fields of atomic update reference type.
- AtomicMarkableReferce: Atom updates reference types with tag bits. You can update tag bits and reference types of a boolean type using a constructor.
public static AtomicReference<User> ai = new AtomicReference<User>(); public static void main(String[] args) { User u1 = new User("pangHu", 18); ai.set(u1); User u2 = new User("piKaQiu", 15); ai.compareAndSet(u1, u2); System.out.println(ai.get().getAge() + ai.get().getName()); } static class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Output result: piKaQiu 15
code analysis
We put the object in AtomicReference, and then call compareAndSet() atomic operation substitution, principle and AtomicInteger is the same, but compareAndSwapObject() is called method.
Atomic update field class
If you need to update a field in the Atomic class, you need to use the Atomic update field class. The Atomic package provides three classes for Atomic field update:
- AtomicIntegerFieldUpdater: An updater that updates fields of an atomic integer.
- AtomicLongFieldUpdater: An updater that updates long fields atomically.
- AtomicStampedFieldUpdater: Atomic updates reference types with version numbers.
//Create an atomic updater and set the object class and object properties that need to be updated private static AtomicIntegerFieldUpdater<User> ai = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); public static void main(String[] args) { User u1 = new User("pangHu", 18); //Atomic renewal age, + 1 System.out.println(ai.getAndIncrement(u1)); System.out.println(u1.getAge()); } static class User { private String name; public volatile int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Code explanation
To update field classes atomically, you need two steps.
- Because 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.
- The fields of the update class must be decorated with public volatile.
Output results:
18
19
Introduction to JDK8 new atomic class
- DoubleAccumulator
- LongAccumulator
- DoubleAdder
- LongAdder
Let's take LongAdder as an example and list the precautions
These classes correspond to improvements to classes such as AtomicLong. For example, LongAccumulator and LongAdder are more efficient than AtomicLong in high concurrency environment.
Atomic and Adder have similar performance in low concurrency environment. However, in the high concurrency environment, Adder has significantly higher throughput, but higher spatial complexity.
LongAdder is actually a special case of LongAccumulator. Calling LongAdder is equivalent to calling LongAccumulator in the following way.
sum() Method is called without concurrency. If it is used in concurrency, there will be inaccurate counting. The following code is an example.
LongAdder cannot replace AtomicLong , Although the add() method of LongAdder can operate atomically, it does not use the CAS algorithm of Unsafe, but uses the idea of CAS.
LongAdder is actually a special case of LongAccumulator. Calling LongAdder is equivalent to calling LongAccumulator in the following way. LongAccumulator provides more powerful functions than LongAdder. Among them, accumulator function is a binocular operator interface, which returns a calculated value according to the two input parameters, and identity is the initial value of LongAccumulator accumulator.
private static ExecutorService executorService = Executors.newFixedThreadPool(5); public static void main(String[] args) { for (int i = 1; i <= 100; i++) { executorService.execute(new Runnable() { @Override public void run() { counter.add(2); } }); } System.out.println(counter.sum()); System.out.println(counter); }
Output results
As shown in the figure, LongAdder internally maintains multiple variables, and each variable is initialized to 0. Under the condition of the same concurrency, the number of threads competing for a single variable will be reduced, which reduces the concurrency of competing for shared resources in a disguised way. In addition, if multiple threads fail to compete for the same atomic variable, they do not try to retry, but try to obtain the locks of other atomic variables, Finally, when obtaining the current value, it is returned after accumulating the values of all variables.
//Constructor LongAdder() //Create a new adder with an initial sum of zero. //Method summary void add(long x) //Adds the given value. void decrement() //Equivalent to add(-1). double doubleValue() //Return sum()as double after extending the original transformation. float floatValue() //After extending the original transformation, return sum()as float. void increment() //Equivalent to add(1). int intValue() //Return sum() as int after a basic contraction conversion. long longValue() //Equivalent to sum(). void reset() //Resets variables that keep the sum to zero. long sum() //Returns the current total. long sumThenReset() //Equivalent to the effect reset() after sum(). String toString() //return. The string representation of sum().