Preface
***
What are the categories?
The question of what an atomic class is is is not explained here, but it is guaranteed that operations on its variables are thread-safe under concurrent conditions. Atomic classes have certain advantages over locks. Its locks are finer granularity and more efficient than locks.
What kinds of atomic classes are there in Java? Give me a table directly.
category | Representation Type |
---|---|
Basic types of atoms | AtomicInteger AtomicLong AtomicBoolean |
Atomic Classes of Array Type | AtomicIntegerArray AtomicLongArray AtomicReferenceArray |
Atomic Classes of Reference Types | AtomicReference AtomicStampedReference AtomicMarkableReference |
Upgraded Atomic Classes | AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater |
Adder accumulator | LongAdder,DoubleAdder |
Accumulator accumulator | LongAccumulator DoubleAccumulator |
The basic type of atoms is the one that wraps up the eight basic types of atoms in our Java.
Atomic classes of array types, in which each element is a basic atomic type.
An atomic class of a reference type, that is, an atomic type referenced to an object
Atomic class of upgrade type, upgrade type for common field type
Adder accumulator, introduced in Java8, Accumulator is an accumulator that adds some functionality to Adder
Basic types
Only the basic uses of AtomicInteger are described in the Basic Types, which include the following methods
public final int get();//Get the current value public final int getAndSet(int newValue);//Get the current value and set a new value public final int getAndIncrement();//Get the current value and increase by itself public final int getAndDecrement();//Get the current value and subtract from it boolean compareAndSet(int expect,int update);//If the value entered equals the expected value, set it atomically as the input value
Instance Code, Comparing Problems with Non-Atomic Classes
** * autor:liman * createtime:2021/11/15 * comment:AtomicInteger A simple example * AtomicInteger * AtomicLong * AtomicBoolean * with AtomicInteger take as an example */ @Slf4j public class AtomicIntegerDemo implements Runnable { private static final AtomicInteger atomicInteger = new AtomicInteger(); private static volatile int basicCount = 0; public void incrementAtomicValue() { atomicInteger.getAndIncrement(); } public void incrementBasicValue() { basicCount++; } @Override public void run() { for (int i = 0; i < 10000; i++) { incrementAtomicValue(); incrementBasicValue(); } } public static void main(String[] args) throws InterruptedException { AtomicIntegerDemo atomicIntegerDemo = new AtomicIntegerDemo(); Thread threaOne = new Thread(atomicIntegerDemo); Thread threadTwo = new Thread(atomicIntegerDemo); threaOne.start(); threadTwo.start(); threaOne.join(); threadTwo.join(); System.out.println("Results of Atomic Classes:"+atomicInteger.get()); System.out.println("Results of Common Classes:"+basicCount); } }
Run Results
Common classes have thread security issues, while atomic classes do not
Array type
Take AtomicIntegerArray for example, where multiple threads operate on the addition or subtraction of each element
/** * autor:liman * createtime:2021/11/16 * comment:AtomicIntegerArray Type of */ @Slf4j public class AtomicArrayDemo { public static void main(String[] args) throws InterruptedException { //Array collection of 1000 AtomicInteger s AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000); Incrementer incrementer = new Incrementer(atomicIntegerArray); Decrementer decrementer = new Decrementer(atomicIntegerArray); //Create 100 new threads to add and 100 threads to subtract Thread[] incrementThread = new Thread[100]; Thread[] decrementThread = new Thread[100]; for(int i=0;i<100;i++){ incrementThread[i] = new Thread(incrementer); decrementThread[i] = new Thread(decrementer); } //Start each thread in turn Arrays.stream(incrementThread).forEach(Thread::start); Arrays.stream(decrementThread).forEach(Thread::start); //Wait for all sub-threads to finish running for(int i=0;i<100;i++){ incrementThread[i].join(); decrementThread[i].join(); } for(int i=0;i<atomicIntegerArray.length();i++){ //An exception is determined if there is a non-zero in the array if(atomicIntegerArray.get(i)!=0){ System.out.println("Discover Exceptions"); } } System.out.println("End of run"); } } //Operate addition on each element in an atomic array class Decrementer implements Runnable { private AtomicIntegerArray array; public Decrementer(AtomicIntegerArray array) { this.array = array; } @Override public void run() { for(int i=0;i<array.length();i++){ array.getAndDecrement(i);//Minus 1 for the first element } } } //Operate subtraction on each element in an atomic array class Incrementer implements Runnable { private AtomicIntegerArray array; public Incrementer(AtomicIntegerArray array) { this.array = array; } @Override public void run() { for(int i=0;i<array.length();i++){ array.getAndIncrement(i);//Minus 1 for the first element } } }
Finally, you can see that in such a complex concurrency scenario, the data is still normal.
reference type
AtomicReference is a simple way to keep an object atomic, similar to AtomicInteger. We can use this class to simply implement a spin lock
** * autor:liman * createtime:2021/11/14 * comment:Simple spin lock example */ public class SpinLockDemo { //Lock or maintain a reference to a thread, indicating the thread holding the lock private AtomicReference<Thread> sign = new AtomicReference<Thread>(); //How threads acquire locks public void lock(){ Thread currentThread = Thread.currentThread(); //Acquire locks via cas. Getting a lock means setting the current sign to the current thread while(!sign.compareAndSet(null,currentThread)){ System.out.println(currentThread.getName()+"Failed to acquire lock, spinning......"); } } //The way the lock is released, the direct cas way is to leave the sign property empty to indicate that you are not held by any threads public void unlock(){ Thread currentThread = Thread.currentThread(); sign.compareAndSet(currentThread,null); } public static void main(String[] args) { SpinLockDemo spinLock = new SpinLockDemo(); Runnable runnable = new Runnable() { @Override public void run() { String currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + "Start trying to acquire a spin lock"); spinLock.lock(); System.out.println(currentThreadName + "Successful spin lock acquisition"); try { //Simulate Business Processing Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } finally { spinLock.unlock(); System.out.println(currentThreadName + "Successful release of spin lock"); } } }; Thread threadOne = new Thread(runnable); Thread threadTwo = new Thread(runnable); threadOne.start(); threadTwo.start(); } }
Normal variable upgraded to atomic variable
Atomic classes in Java, in addition to the Atomic class variables and attributes already provided, have tools to upgrade ordinary variables directly to atomic variables, which requires the FieldUpdater in Atomic
Or use AtomicIntegerFieldUpdater as an example
/** * autor:liman * createtime:2021/11/16 * comment:Upgrade a common variable to an atomic variable */ public class AtomicIntegerFieldUpdateDemo implements Runnable{ static Candidate tom; static Candidate peter; //When constructed, the first parameter is Class and the second is the property name within it public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater =AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score"); //For example, static internal class public static class Candidate{ volatile int score; } @Override public void run() { for(int i=0;i<10000;i++){ peter.score++; //Self-Increasing with Atomic Variables scoreUpdater.getAndIncrement(tom); } } public static void main(String[] args) throws InterruptedException { tom = new Candidate(); peter = new Candidate(); AtomicIntegerFieldUpdateDemo runnable = new AtomicIntegerFieldUpdateDemo(); Thread threadOne = new Thread(runnable); Thread threadTwo = new Thread(runnable); threadOne.start(); threadTwo.start(); threadOne.join(); threadTwo.join(); System.out.println("Common variable values:"+peter.score); System.out.println("Variable values upgraded to atomic classes:"+tom.score); } }
The example is simple, but the AtomicIntegerFieldUpdater.newUpdater method, which supports attributes that can be accessed directly through reflection, is not supported if the attribute is declared private. Attributes declared as static are also not supported and cannot be upgraded to atomic variables.
Adder accumulator
This was introduced by Java8, a newer product. LongAdder is much more efficient than AtomicLong in high concurrency scenarios. This example illustrates the basic use of Adder
Example of using AtomicLong for cardinality
/** * autor:liman * createtime:2021/11/16 */ @Slf4j public class AtomicLongDemo { public static void main(String[] args) { AtomicLong atomicLongCounter = new AtomicLong(0); ExecutorService executorService = Executors.newFixedThreadPool(20); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(atomicLongCounter)); } //Determine whether the thread pool is stopped executorService.shutdown(); while(!executorService.isTerminated()){ //When the thread pool does not perform the completed task, the main thread is idle until the end of the thread pool operation } long endTime = System.currentTimeMillis(); System.out.println("Time taken for tests"+(endTime-startTime)); System.out.println(atomicLongCounter.get()); } private static class Task implements Runnable{ private AtomicLong atomicLongCounter; public Task(AtomicLong atomicLongCounter) { this.atomicLongCounter = atomicLongCounter; } @Override public void run() { for(int i=0;i<10000;i++){ atomicLongCounter.incrementAndGet(); } } } }
Run time: 1229 MS
Same functionality, done with LongAdder
/** * autor:liman * createtime:2021/11/16 * comment:LongAdder Accumulator instance * LongAdder Better than AtomicLong */ @Slf4j public class LongAdderDemo { public static void main(String[] args) { LongAdder longAdderCounter = new LongAdder(); ExecutorService executorService = Executors.newFixedThreadPool(20); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(longAdderCounter)); } //Determine whether the thread pool is stopped executorService.shutdown(); while(!executorService.isTerminated()){ //The main thread is idle when the thread pool does not perform the completed task } long endTime = System.currentTimeMillis(); System.out.println("Time taken for tests"+(endTime-startTime)); System.out.println(longAdderCounter.sum()); } private static class Task implements Runnable{ private LongAdder longAdderCounter; public Task(LongAdder longAdderCounter) { this.longAdderCounter = longAdderCounter; } @Override public void run() { for(int i=0;i<10000;i++){ longAdderCounter.increment(); } } } }
115 MS elapsed
In principle, AtomicLong synchronizes data into shared memory every time it modifies it
LongAdder does not. Each thread has its own counters, but it only counts the final results once, summarizes the local count values for each thread, and then synchronizes the final results to shared memory. Such optimization takes a short time.
Accumulator accumulator
This is similar to LongAdder, but more general and slightly more powerful.
Simple 1-100 sum, which runs in a multi-threaded fashion and is simpler than traditional traversal
/** * autor:liman * createtime:2021/11/16 * comment:Demonstrate Accumulate accumulator */ @Slf4j public class LongAccumulatorDemo { public static void main(String[] args) { LongAccumulator accumulator = new LongAccumulator((x,y)->x+y,0); ExecutorService executorService = Executors.newFixedThreadPool(8); IntStream.rangeClosed(1,100).forEach(t->executorService.submit(()->accumulator.accumulate(t))); executorService.shutdown(); //Waiting for thread pool to end, main thread spins while(!executorService.isTerminated()){ } System.out.println(accumulator.getThenReset());//This outputs the cumulative results of 1~100, which are obtained when running on multiple threads } }
summary
Summary of the simple use of atomic classes. Next CAS