@[TOC]

Atomic Atomic classes


1. Introduction to atomic classes

  1. indivisible

  2. 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.

  3. 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
  1. 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.
  2. 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
  1. 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
  2. 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

  1. Their internal implementation is a little different, AtomicLong needs to synchronize every addition, so there are more conflicts, which reduces efficiency
  2. LongAdder, on the other hand, each thread has its own counter, which is only used for counting threads and will not disturb other threads
  3. AtomicLong introduces the concept of segmented locking, with a base variable and an array of cells [] participating in the count
  4. Base variable: the competition is not fierce and directly adds to the variable
  5. 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”