@[TOC]
Atomic Atomic classes
1. Introduction to atomic classes
-
indivisible
-
An operation is not interruptible, even in the case of multi-threading, even when multiple threads are executing together, once an operation is started, it will not be interfered by other threads.
-
Atomic classes are similar to locks in that they are thread-safe in the case of concurrency, but have more advantages than locks
Advantages: finer granularity, higher efficiency
Atomic Class Overview:
type | Value |
---|---|
Atomic* Basic type Atomic class | AtomicInteger AtomicLong AtomicBoolean |
Atomic*Arrays Type Atomic class | AtomicIntegerArray AtomicLongArray AtomicReferenceArray |
Atomic*Reference Type Atomic class | AtomicReference AtomicStampedReference AtomicMarkableReference |
Atomic*Fieldupdate Upgrade type Atomic class | AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater |
Adder accumulator | LongAdder DoubleAdder |
An Accumulator Accumulator | LongAccumulator DoubleAccumulator |
2. Basic types atomic classes
- AtomicInteger: Integer atomic class
- AtomicLong: long integer atomic class
- AtomicBoolean: Boolean atomic class
AtomicInteger, for example
AtomicInteger class:
public final int get(a) / / get the value
public final void set(int newValue) / / set the value
public final void lazySet(int newValue) // Finally set to the given value
public final int getAndSet(int newValue) // Get the current value and set the new value
public final int getAndIncrement(a) // Get the current value and increment it
public final int getAndDecrement(a) // Get the current value and decrement it
public final int getAndAdd(int delta) // Get the current value and add the expected value
public final int getAndAdd(int delta) // Get the current value and add the expected value
public final boolean compareAndSet(int expect, int update) // Compare and replace
Copy the code
Use:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest {
public static void main(String[] args) {
int temvalue = 0;
AtomicInteger i = new AtomicInteger(0);
temvalue = i.getAndSet(3);
System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3
temvalue = i.getAndIncrement();
System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4
temvalue = i.getAndAdd(5);
System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9}}Copy the code
Case study:
public class Test {
public static void main(String[] args) {
TestDemo thread = new TestDemo();
Thread t1 = new Thread(thread,"Window one");
Thread t2 = new Thread(thread,"Window two"); t1.start(); t2.start(); }}class TestDemo implements Runnable{
// Shared train ticket variables
private int count = 100;
// Override the run method
@Override
public void run(a) {
while (count > 0) {try {
// Hibernate for concurrency problems
Thread.sleep(50);
}catch(Exception e){ e.getMessage(); } sale(); }}/ / tickets
public void sale(a){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"For sale:"+ (100 - count + 1)); count--; }}}Copy the code
Window one sale: 37
Window 2 for Sale: 39 Window 1 for sale: 40 Window 2 for sale: 41 Window 1 for sale: 41 Window 1 for sale: 43 Window 2 for sale: 43 Window 1 for sale: 45 Window 2 for sale: 45 Window 2 for sale: 47 Window 2 for sale: 47
Synchronized can be used to ensure atomicity of threads. When a thread acquires the lock, other threads will wait, but the performance is low. AtomicInteger class can be used. Is a class that specializes in providing guaranteed atomicity
package com.dimple.test;
import java.util.concurrent.atomic.AtomicInteger;
public class Test5 {
public static void main(String[] args) {
TestDemo thread = new TestDemo();
Thread t1 = new Thread(thread,"Window one");
Thread t2 = new Thread(thread,"Window two"); t1.start(); t2.start(); }}class TestDemo implements Runnable{
// Shared train ticket variables
private static AtomicInteger atomic = new AtomicInteger(100);
// Override the run method
@Override
public void run(a) {
while (atomic.get() > 0) {try {
// Hibernate for concurrency problems
Thread.sleep(50);
}catch(Exception e){ e.getMessage(); } sale(); }}/ / tickets
public void sale(a){
if(atomic.get() > 0){
Integer count= 100 - atomic.getAndDecrement() + 1; // Use the lower-level method getAndDecrement() since -1;
System.out.println(Thread.currentThread().getName()+ "," + count);// Get the current value}}}Copy the code
Window one for sale: 91
Window 2 for sale: 92 Window 1 for sale: 93 Window 2 for sale: 94 Window 1 for sale: 95 Window 2 for sale: 96 Window 1 for sale: 97 Window 2 for sale: 98 window 1 for sale: 99 window 2 for sale: 100
Principle analysis:
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw newError(ex); }}private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
Copy the code
- AtomicInteger class mainly uses CAS (compare and swap) + volatile native methods to ensure atomic operations, thus avoiding the high overhead of synchronized and greatly improving the execution efficiency.
- The UnSafe class’s objectFieldOffset() method is a native method to retrieve the memory address of the “original value.” And value is a volatile variable that is visible in memory, so the JVM can ensure that the thread gets the latest value of the variable at any time.
3. Array type atomic class
- AtomicIntegerArray: Integer array atomic class
- AtomicLongArray: Long integer array atomic class
- AtomicReferenceArray: Reference type array atomic class
AtomicIntegerArray, for example
Common methods of the AtomicIntegerArray class
public final int get(int i) / / get the value
public final void set(int i, int newValue) / / set the value
public final void lazySet(int i, int newValue) // The element of the final set is at position I to the given value.
public final int getAndSet(int i, int newValue) // Automatically sets the element's position I to the given value and returns the old value.
public final boolean compareAndSet(int i, int expect, int update) // Automatically sets element position I to updated value if expected value == expected value.
public final int getAndIncrement(int i) // Automatically increments an exponential I element.
public final int getAndDecrement(int i) // Automatically decrement the index I element
Copy the code
Use:
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayTest {
public static void main(String[] args) {
int temvalue = 0;
int[] nums = { 1.2.3.4.5.6 };
AtomicIntegerArray i = new AtomicIntegerArray(nums);
for (int j = 0; j < nums.length; j++) {
System.out.println(i.get(j));
}
temvalue = i.getAndSet(0.2);
System.out.println("temvalue:" + temvalue + "; i:" + i);
temvalue = i.getAndIncrement(0);
System.out.println("temvalue:" + temvalue + "; i:" + i);
temvalue = i.getAndAdd(0.5);
System.out.println("temvalue:" + temvalue + "; i:"+ i); }}Copy the code
Case study:
public class AtmoicArray {
public static void main(String[] args) throws InterruptedException {
AtomicIntegerArray atomicIntegerArray=new AtomicIntegerArray(1000);
Decrement decrement = new Decrement(atomicIntegerArray);
Increment increment = new Increment(atomicIntegerArray);
Thread[] threads= new Thread[100];
Thread[] threads2= new Thread[100];
for (int i = 0; i < 100 ; i++) {
threads2[i]=new Thread(decrement);
threads[i]=new Thread(increment);
threads2[i].start();
threads[i].start();
}
for (int i = 0; i < 100 ; i++) {
threads2[i].join();
threads[i].join();
}
for (int i = 0; i < atomicIntegerArray.length(); i++) {
if(atomicIntegerArray.get(i)! =0) {
System.out.println("Non-zero value found" + i);
}
}
System.out.println("End of run"); }}class Decrement implements Runnable{
private AtomicIntegerArray array;
Decrement(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run(a) {
for (int i = 0; i < array.length() ; i++) { array.getAndDecrement(i); }}}class Increment implements Runnable{
private AtomicIntegerArray array;
Increment(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run(a) {
for (int i = 0; i < array.length() ; i++) { array.getAndIncrement(i); }}}Copy the code
End of the run
When we run the result, we see that every time we add 100 or subtract 100, we don’t get data that’s not equal to zero, we don’t get data that’s messed up, and the AtomicIntegerArray gives us the atomicity of the array
Principle analysis:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int shift;
private final int[] array;
static {
int scale = unsafe.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
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;
}
Copy the code
- Unsafe.arraybaseoffset gets the address of the first element in an array, relative to the initial memory offset of the array object, and unsafe.arrayIndexSacle(int[].class) gets the size of the elements in the array. How many bytes are occupied
- Int scale = 4; int scale = 4; An int type, four bytes in Java, Integer. NumberOfLeadingZeros (scale); Scale returns the number of consecutive zeros at the high level, resulting in shift = 2. Shift is used to locate memory in the array
4. Reference type atomic class
The primitive atomic class can update only one variable, but if you need to update multiple variables atomically, you need to use the reference type atomic class
- AtomicReference: Reference type atomic class
- AtomicStampedReference: Atom updates a reference type with a version number
- AtomicMarkableReference: Atom updates reference types with tags
Common methods of AtomicReference
public final V get(a) / / get the value
public final void set(V newValue) / / set the value
public final void lazySet(V newValue) // Finally set to the given value
public final boolean compareAndSet(V expect, V update) // Automatically set the value to specify the update value
public final V getAndSet(V newValue) // Automatically set to the given value and return the old value.
public final V getAndUpdate(UnaryOperator<V> updateFunction) // Automatically updates the current value with the result. Returns the previous value.
Copy the code
AtomicReference to use
public class Test {
public static void main(String[] args) {
AtomicReference<Person> atomicReference = new AtomicReference<Person>();
Person person = new Person("abc".22);
atomicReference.set(person);
Person updatePerson = new Person("Daisy".20); atomicReference.compareAndSet(person, updatePerson); System.out.println(atomicReference.get().getName()); System.out.println(atomicReference.get().getAge()); }}@Data
class Person {
private String name;
private int age;
}
Copy the code
Daisy
20
AtomicStampedReference use
public class Test {
public static void main(String[] args) {
// take the current value and stamp value
final Integer initialRef = 0, initialStamp = 0;
final AtomicStampedReference<Integer> asr = new AtomicStampedReference<Integer>(initialRef, initialStamp);
System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());
// compare and set
final Integer newReference = Awesome!, newStamp = 999;
final boolean casResult = asr.compareAndSet(initialRef, newReference, initialStamp, newStamp);
System.out.println("currentValue=" + asr.getReference()
+ ", currentStamp=" + asr.getStamp()
+ ", casResult=" + casResult);
// Get the current value and current stamp value
int[] arr = new int[1];
final Integer currentValue = asr.get(arr);
final int currentStamp = arr[0];
System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp);
// Set stamp value separately
final boolean attemptStampResult = asr.attemptStamp(newReference, 88);
System.out.println("currentValue=" + asr.getReference()
+ ", currentStamp=" + asr.getStamp()
+ ", attemptStampResult=" + attemptStampResult);
// Reset the current value and stamp value
asr.set(initialRef, initialStamp);
System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());
}
Copy the code
currentValue=0, currentStamp=0
currentValue=666, currentStamp=999, casResult=true currentValue=666, currentStamp=999 currentValue=666, currentStamp=88, attemptStampResult=true currentValue=0, currentStamp=0
AtomicMarkableReference use
public class Test {
public static void main(String[] args) {
// instantiate and take the current and mark values
final Boolean initialRef = null, initialMark = false;
final AtomicMarkableReference<Boolean> amr = new AtomicMarkableReference<Boolean>(initialRef, initialMark);
System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
// compare and set
final Boolean newReference1 = true, newMark1 = true;
final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1);
System.out.println("currentValue=" + amr.getReference()
+ ", currentMark=" + amr.isMarked()
+ ", casResult=" + casResult);
// Get the current value and mark value
boolean[] arr = new boolean[1];
final Boolean currentValue = amr.get(arr);
final boolean currentMark = arr[0];
System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark);
// Set the mark value separately
final boolean attemptMarkResult = amr.attemptMark(newReference1, false);
System.out.println("currentValue=" + amr.getReference()
+ ", currentMark=" + amr.isMarked()
+ ", attemptMarkResult=" + attemptMarkResult);
// Reset the current and mark values
amr.set(initialRef, initialMark);
System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
}
Copy the code
currentValue=null, currentMark=false
currentValue=true, currentMark=true, casResult=true currentValue=true, currentMark=true currentValue=true, currentMark=false, attemptMarkResult=true currentValue=null, currentMark=false
5. Upgrade type atomic classes
- AtomicIntegerFieldUpdater: atomic updates plastic field updater
- AtomicLongFieldUpdater: An updater that atomically updates long shaping fields
- AtomicReferenceFieldUpdater: atomic updates the fields in the reference type updater
Methods AtomicIntegerFieldUpdater
public final V get(a) / / get the value
public final void set(V newValue) / / set the value
public final void lazySet(V newValue) // Finally set to the given value
public final boolean compareAndSet(V expect, V update) // Automatically set the value to specify the update value
public final V getAndSet(V newValue) // Automatically set to the given value and return the old value.
public final V getAndUpdate(UnaryOperator<V> updateFunction) // Automatically updates the current value with the result. Returns the previous value.
public final V getAndAccumulate(T obj, V x, BinaryOperator
accumulatorFunction)
// Automatically updates and applies the given function value and the result index I element of the given value, returns the previous value.
public final V accumulateAndGet(T obj, V x,BinaryOperator<V> accumulatorFunction) // Automatically updates and applies the given function value and the result index I element of the given value, returns the updated value.
Copy the code
AtomicIntegerFieldUpdater use
public class Test {
public static void main(String[] args) {
AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User("Java".22);
System.out.println(a.getAndIncrement(user));/ / 22
System.out.println(a.get(user));/ / 23}}@Data
class User {
private String name;
public volatile intThe age; }Copy the code
22
23
It takes two steps to update an object’s properties atomically. First, because the object’s attribute modification type atomic classes are abstract classes, each use must create a updater using the static method newUpdater(), and you need to set the class and properties that you want to update. Second, the updated object property must use the public volatile modifier.
6. Adder accumulator
- Is a relatively new class introduced in JDK 1.8
- LongAdder is more efficient than AtomitLong at high concurrency, but space for time
- When competition is fierce, LingAdder will modify different threads on different cells, which reduces the probability of conflicts. It is the concept of multi-segment lock, which improves the concurrency
Test AtomicLong’s performance
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
/** * demonstrates that LongAdder performs better than AtomicLong */ for high concurrency
public class AtomicLongDemo {
public static void main(String[] args) throws InterruptedException {
AtomicLong atomicLong = new AtomicLong(0);
// Start time of the thread pool
long start = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(20);
for (int i = 0; i < 10000; i++) {
executorService.submit(new Task(atomicLong));
}
// Indicates that the thread pool is complete
executorService.shutdown();
while(! executorService.isTerminated()){ }long end = System.currentTimeMillis();
System.out.println(atomicLong.get());
System.out.println("Take"+(end -start));
}
public static class Task implements Runnable{
private AtomicLong atomicLong;
public Task(AtomicLong atomicLong) {
this.atomicLong = atomicLong;
}
@Override
public void run(a) {
for (int i = 0; i < 10000; i++) { atomicLong.incrementAndGet(); }}}}Copy the code
Test LongAdder performance
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/** * demonstrates that LongAdder performs better than AtomicLong */ for high concurrency
public class LongAdderDemo {
public static void main(String[] args) throws InterruptedException {
LongAdder atomicLong = new LongAdder();
// Start time of the thread pool
long start = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(20);
for (int i = 0; i < 10000; i++) {
executorService.submit(new Task(atomicLong));
}
// Indicates that the thread pool is complete
executorService.shutdown();
while(! executorService.isTerminated()){ }long end = System.currentTimeMillis();
System.out.println(atomicLong.sum() );
System.out.println("Take"+(end -start));
}
public static class Task implements Runnable{
private LongAdder longAdder;
public Task(LongAdder longAdder) {
this.longAdder = longAdder;
}
@Override
public void run(a) {
for (int i = 0; i < 10000; i++) { longAdder.increment(); }}}}Copy the code
LongAdder is much faster than AtomicLong
- Their internal implementation is a little different, AtomicLong needs to synchronize every addition, so there are more conflicts, which reduces efficiency
- LongAdder, on the other hand, each thread has its own counter, which is only used for counting threads and will not disturb other threads
- AtomicLong introduces the concept of segmented locking, with a base variable and an array of cells [] participating in the count
- Base variable: the competition is not fierce and directly adds to the variable
- Cell [] array: Competing threads add themselves to the cell[I] slot
7. An Accumulator
- Accumualtor and Adder are very similar, and Accumualtor is the more general version of Adder
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.stream.IntStream;
public class LongAccumulatorDemo {
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator((x,y)->x+y,0);
ExecutorService executorService = Executors.newFixedThreadPool(8);
IntStream.range(1.10).forEach(i->executorService.submit(()->accumulator.accumulate(i)));
executorService.shutdown();
while(! executorService.isTerminated()) System.out.println(accumulator.getThenReset()); }Copy the code
45
“When there is a high wind, life does not give up”