background
Multithreading updates the value of variables, which may not get the expected value. Of course, adding the synchronized keyword can solve the problem of thread concurrency.
Another solution is provided here, that is, the atomic operation class under the java.util.concurrent.atomic package, which provides a simple, efficient and thread safe way to update variables.
The other two additional classes take a look by default:
Long addr multithreaded sum operation
The performance of the function operation under the multithreading of the long emulator is lower than that of the AtomicLong, mainly because of the function support;
Simple classification:
Basic type atomic class
Update basic types using atoms, including:
- AtomicBoolean
- AtomicInteger
- AtomicLong
Core approach:
Look directly at the source code.
Class signature:
public class AtomicInteger extends Number implements java.io.Serializable {}
Method | Function description |
---|---|
Construction method | Two construction methods, do not pass or pass in value |
get method | get() gets the value; the corresponding set(int) method, layzySet(int) lazy setting |
getAndAdd(int) | Get the old value and then add a number, corresponding to addAndGet(int), add a number and return the new value |
getAndSet(int) | Get old value and update to new value |
getAndIncreament() | Get the old value and then + 1, corresponding to incrementandget() + 1 and return the new value |
getAndDecrement() | Get the old value and then - 1, corresponding to incrementandget() - 1 and return the new value |
getAndUpdate(IntUnaryOperator) | Get the old value and execute a function to get the new value and set it. The corresponding function is updateAndGet(IntUnaryOperator). First execute the built-in function interface and then return the new value |
getAndAccumulate(int,IntBinaryOperator) | Get the old value, and then set the return value of the old value and the first parameter for function operation. The corresponding one is accumulaedandget (int, intbinaryoperator) to perform the operation and then return the new value |
compareAndSet(int,int) | If the comparison is equal to the expected value, it is set to a new value. The corresponding value is weakCompareAndSet(int,int), which is not guaranteed to be set in sequence |
toString | Returns the string form of a number |
number inherited method | longValue(),byteValue() does type conversion directly |
Methods inherited from object | Directly follow the method of Object |
The bottom layer is implemented based on unsafe and atomicity based on CAS;
To study the implementation source code of unsafe:
/** * Atomically decrements by one the current value. * * @return the previous value */ public final int getAndDecrement() { 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; }
The general processing flow is: dead cycle, comparison; that is CAS;
Using the characteristics of volatile, multi-threaded variable visibility;
Atomic array
Update an element in the array by atom;
There are three classes:
- AtomicIntegerArray
- AtomicIntegerLongArray
- AtomicReferenceArray
Grasp a class to analyze:
public class AtomicIntegerArray implements java.io.Serializable {}
Method | Explain |
---|---|
Construction method | public AtomicIntegerArray(int length), public AtomicIntegerArray(int[] array) a clone will be made here, which does not affect the value of the incoming array |
length | Get the length of the inner array |
get,set,layziset | Get, set, lazy settings |
compareAndSet,weakCompareAndSet | CAS operation, peak method does not guarantee the order of operation |
getAndAdd,getAndUpdate,getAndAccumulate | There is a reverse way, which is to calculate first and then return the new value |
toString | Print out the array [number 1, number 2] |
The operation of atomic type is special:
/** * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add * @return the previous value */ public final int getAndAdd(int i, int delta) { return unsafe.getAndAddInt(array, checkedByteOffset(i), delta); }
private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); return byteOffset(i); } private static long byteOffset(int i) { return ((long) i << shift) + base; }
A shift operation is used to get the values in the array;
Update reference
Atomic update class AtomicInterger can only update one variable. If you want to update multiple different variables, you need to update the class provided by the reference type;
- AtomicReference update reference type
- AtomicReferenceFieldUpdater updates fields of reference type
- Atomicmarkablerenewal updates reference types with flags
Take AtomicReference as an example:
Signature: public class AtomicReference implements java.io.Serializable {}
Method:
Method | Explain |
---|---|
Construction method | public AtomicReference(V initialValue) with initial value; public AtomicReference() |
get,set,lazySet | Settings, get, lazy settings |
compareAndSet,weakCompareAndSet | CAS operation, the week method does not guarantee the order |
getAndSet,getAndUpdate,getAndAccumulate | Reverse operation |
toString | Print out the objects inside |
Underlying analysis:
/** * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value */ @SuppressWarnings("unchecked") public final V getAndSet(V newValue) { return (V)unsafe.getAndSetObject(this, valueOffset, newValue); }
The characteristics of unsafe are used to ensure the atomic operation;
Atomic update field
To update a field of a class, you need to update the field class with atoms;
- Atomicintegerfiledapdater needless to say, the inter field of the atomic update class
- AtomicLongFieldUpdater needless to say, the atom updates the Long field of the class
- AtomicStampedReference atom updates the reference type with version number. It can update the reference and the version number of the reference atom to solve the ABA problem;
Key points:
- Each time, you must use the static method newUpdater to create an updater and set classes and properties;
- The properties of the updated class must be decorated with public volatile;
package com.cocurrenttest.atomictest; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * Description: human entity * @author carter * Creation time: 19:27, December 6, 2019 **/ @Data @NoArgsConstructor @AllArgsConstructor @Builder public class Person { private String name; //Note, it can only be int. integer will report an error public volatile int age; }
package com.cocurrenttest.atomictest; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; /** * Description: TODO * @author carter * Creation time: 19:26, December 6, 2019 **/ public class TestAtomicIntegerUpdater { public static void main(String[] args) { final AtomicIntegerFieldUpdater<Person> personAtomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age"); Person person = Person.builder().name("lifuchun").age(30).build(); personAtomicIntegerFieldUpdater.addAndGet(person,1); final int age = personAtomicIntegerFieldUpdater.get(person); System.out.println(age); assert age==31 : "Update failed"; } }
Summary
Atomic operation classes are introduced. For the appropriate scenarios, I'll briefly talk about the two scenarios I used:
- Reconciliation during multi task data copy to calculate the total number of modified lines or the total amount of migrated orders for comparison between the two sides;
- The local variable that needs to be passed in the lambda expression of flow operation is final, but the general type is not final in the method, and it needs to be modified in the middle process. The IDE prompts that atomic class can be used to wrap it, and then take final to modify it;
package com.cocurrenttest.atomictest; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** * Description: TODO * @author carter * Creation time: 19:36, December 6, 2019 **/ public class TestStream { public static void main(String[] args) { Integer age = 25 ; final String name="b"; //some condition to change name ="bbb"; final List<Person> personList = Arrays.asList( Person.builder().name("aaa").age(10).build(), Person.builder().name("bbb").age(20).build(), Person.builder().name("ccc").age(30).build() ) .stream() .filter(item -> item.getAge() >= age) .filter(item->item.getName().contains(name)) .collect(Collectors.toList()); System.out.println(personList); } public static void main2(String[] args) { Integer age = 25 ; final AtomicReference<String> name=new AtomicReference<>("b"); //some condition to change name.set("bbb");; final List<Person> personList = Arrays.asList( Person.builder().name("aaa").age(10).build(), Person.builder().name("bbb").age(20).build(), Person.builder().name("ccc").age(30).build() ) .stream() .filter(item -> item.getAge() >= age) .filter(item->item.getName().contains(name.get())) .collect(Collectors.toList()); System.out.println(personList); } }
Original is not easy, reprint please indicate the source.