Java. Util. Concurrent. Atomic classification:

  • Base type atomic class
    • AtomicInteger
    • AtomicLong
    • AtomicBoolean
  • Array type atomic class
    • AtomicIntegerArray
    • AtomicLongArray
    • AtomicReferenceArray
  • Reference type atomic class
    • AtomicReference
    • AtomicMarkableReference
    • AtomicStampedReference
  • Object to modify the properties of the atomic class
    • AtomicIntegerFieldUpdater
    • AtomicLongFieldUpdater
    • AtomicReferenceFieldUpdater
  • Atomic manipulation enhancement classes
    • DoubleAccumulator
    • DoubleAdder
    • LongAccumulator
    • LongAdder

AtomicStampedReference and AtomicMarkableReference

An AtomicStampedReference is a reference type atom class with a version number that addresses an ABA problem and records how many times it has been modified

AtomicMarkableReference is the version number of an AtomicStampedReference, reduced to true or false, and can only be used once

Use the sample

    1. Base type atomic class, take AtomicInteger as an example
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; Public class AtomicTest {/** * 50 threads */ private static final int THREAD_NUM = 50; private static final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM); public static void main(String[] args) throws InterruptedException { MyNumber myNumber = new MyNumber(); for (int i = 0; i < THREAD_NUM; i++) { new Thread(() -> { try { for (int j = 0; j < 1000; j++) { myNumber.add(); } } catch (Exception e) { e.printStackTrace(); } finally { countDownLatch.countDown(); } }).start(); } countDownLatch.await(); System.out.println(" final result: "+ mynumber.num.get ()); } } class MyNumber { AtomicInteger num = new AtomicInteger(); public void add() { num.getAndIncrement(); }}Copy the code
    1. Array type atomic class, take AtomicIntegerArray as an example
public static void main(String[] args) { AtomicIntegerArray atomicIntegerArray1 = new AtomicIntegerArray(5); AtomicIntegerArray atomicIntegerArray2 = new AtomicIntegerArray(new int[5]); AtomicIntegerArray atomicIntegerArray3 = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5}); for (int i = 0; i < atomicIntegerArray3.length(); I ++) {system.out.println (atomicIntegerArray3.get(I)); } int temp = 0; atomicIntegerArray3.getAndSet(0, 111); System.out.println(atomicIntegerArray3.get(0)); // +1 atomicIntegerArray3.getAndIncrement(1); // +1 atomicIntegerArray3.getAndIncrement(1); Println (atomicIntegerArray3.get(1)); }Copy the code
    1. Reference type atomic class, AtomicReference as an example to achieve a spin lock
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void MyLock() {
        System.out.println(Thread.currentThread().getName() + " come in");
        while (!atomicReference.compareAndSet(null, Thread.currentThread())) {

        }
        System.out.println(Thread.currentThread().getName() + " 获取锁成功");
    }

    public void MyUnLock() {
        while (!atomicReference.compareAndSet(Thread.currentThread(), null)) {

        }
        System.out.println(Thread.currentThread().getName() + " 释放锁");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            // 获取锁
            spinLockDemo.MyLock();
            try {
                // 拿到锁使用3秒钟
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 释放锁
            spinLockDemo.MyUnLock();
        }, "t1").start();

        new Thread(() -> {
            spinLockDemo.MyLock();
            spinLockDemo.MyUnLock();
        }, "t2").start();
    }

}
Copy the code

The output

T1 Come in T1 The lock was obtained successfully t2 come in T1 Release the lock T2 The lock was obtained successfully T2 release the lockCopy the code
    1. Object properties modify atomic class,Manipulating certain fields in a non-thread-safe object in a thread-safe manner

    Usage requirements: Updated object attributes must be public volatile

AtomicIntegerFieldUpdater using the sample

import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class AtomicIntegerFieldUpdaterDemo { public static void main(String[] args) throws InterruptedException { BankAccount bankAccount = new BankAccount(); for (int i = 0; i < 1000; i++) { new Thread(() -> { bankAccount.doAction(bankAccount); }).start(); } TimeUnit.SECONDS.sleep(1); // print 1000 system.out.println (bankAccount.money); }} class BankAccount {public String bankName = "bankName "; public volatile int money; / / object is obtained through reflection AtomicIntegerFieldUpdater < now > updater. = AtomicIntegerFieldUpdater newUpdater (now. Class, "money"); public void doAction(BankAccount bankAccount) { updater.incrementAndGet(bankAccount); }}Copy the code
    1. Atomic manipulation enhancement classes

LongAdder: Can only be used to calculate addition and start from zero

LongAdder longAdder = new LongAdder(); longAdder.increment(); longAdder.increment(); longAdder.increment(); 3 system.out.println (longadder.longValue ());Copy the code

LongAccumulator: Provides custom function operations

LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0); longAccumulator.accumulate(1); longAccumulator.accumulate(2); longAccumulator.accumulate(3); / / output 6 System. Out. Println (longAccumulator. LongValue ());Copy the code

Atomic high performance comparison

Example: Using 50 threads, each thread accumulates 1 million times and outputs the result (similar to high concurrent likes)

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; class ClickNumber { int number = 0; public synchronized void addBySynchronized() { number++; } AtomicInteger atomicInteger = new AtomicInteger(0); public void addByAtomicInteger() { atomicInteger.incrementAndGet(); } AtomicLong atomicLong = new AtomicLong(0); public void addByAtomicLong() { atomicLong.incrementAndGet(); } LongAdder longAdder = new LongAdder(); public void addByLongAdder() { longAdder.increment(); } LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0); public void addByLongAccumulator() { longAccumulator.accumulate(1); } } public class LongAdderDemo { public static void main(String[] args) throws InterruptedException { ClickNumber clickNumber = new ClickNumber(); long startTime; long endTime; CountDownLatch countDownLatch1 = new CountDownLatch(50); CountDownLatch countDownLatch2 = new CountDownLatch(50); CountDownLatch countDownLatch3 = new CountDownLatch(50); CountDownLatch countDownLatch4 = new CountDownLatch(50); CountDownLatch countDownLatch5 = new CountDownLatch(50); startTime = System.currentTimeMillis(); For (int I = 1; i <= 50; i++) { new Thread(() -> { try { for (int j = 1; j <= 1000000; J++) {/ / each thread accumulate 1 million clickNumber addBySynchronized (); } } finally { countDownLatch1.countDown(); } }, String.valueOf(i)).start(); } countDownLatch1.await(); endTime = System.currentTimeMillis(); System.out.println("synchronized time "+ (endtime-startTime) +" milliseconds "+ clickNumber.number); startTime = System.currentTimeMillis(); for (int i = 1; i <= 50; i++) { new Thread(() -> { try { for (int j = 1; j <= 1000000; j++) { clickNumber.addByAtomicInteger(); } } finally { countDownLatch2.countDown(); } }, String.valueOf(i)).start(); } countDownLatch2.await(); endTime = System.currentTimeMillis(); System. Out. Println (" AtomicInteger time-consuming "+ (endTime - startTime)," ms "+ clickNumber. AtomicInteger. The get ()); startTime = System.currentTimeMillis(); for (int i = 1; i <= 50; i++) { new Thread(() -> { try { for (int j = 1; j <= 1000000; j++) { clickNumber.addByAtomicLong(); } } finally { countDownLatch3.countDown(); } }, String.valueOf(i)).start(); } countDownLatch3.await(); endTime = System.currentTimeMillis(); System. Out. Println (" AtomicLong time-consuming "+ (endTime - startTime)," ms "+ clickNumber. AtomicLong. The get ()); startTime = System.currentTimeMillis(); for (int i = 1; i <= 50; i++) { new Thread(() -> { try { for (int j = 1; j <= 1000000; j++) { clickNumber.addByLongAdder(); } } finally { countDownLatch4.countDown(); } }, String.valueOf(i)).start(); } countDownLatch4.await(); endTime = System.currentTimeMillis(); System. Out. Println (" LongAdder time-consuming "+ (endTime - startTime)," ms "+ clickNumber. LongAdder. LongValue ()); startTime = System.currentTimeMillis(); for (int i = 1; i <= 50; i++) { new Thread(() -> { try { for (int j = 1; j <= 1000000; j++) { clickNumber.addByLongAccumulator(); } } finally { countDownLatch5.countDown(); } }, String.valueOf(i)).start(); } countDownLatch5.await(); endTime = System.currentTimeMillis(); System. Out. Println (" LongAccumulator time-consuming "+ (endTime - startTime)," ms "+ clickNumber. LongAccumulator. LongValue ()); }}Copy the code

As can be seen from the output results, LongAdder performance is significantly higher than others

Synchronized 941 ms 50000000 AtomicInteger 980 ms 50000000 AtomicLong 984 ms 50000000 LongAdder 153 ms 50000000 LongAccumulator takes 262 milliseconds 50000000Copy the code

Why is LongAdder faster than AtomicLong?

  1. The official documentation explains that AtomicLong is similar to LongAdder in the case of fewer threads competing, but at higher concurrency LongAdder has significantly higher throughput at the expense of higher space consumption. That’s space for time

  2. The AtomicInteger and AtomicLong parent classes are Number. The LongAdder parent class is Striped64

public class AtomicLong extends Number

public class LongAdder extends Striped64

abstract class Striped64 extends Number
Copy the code

In the Striped64 source code, there is the following definition

/** * Number of CPUS, To place bound on table size * number of CPU cores */ static final int NCPU = Runtime.getruntime ().availableprocessors (); /** * Table of cells. When non-null, size is a power of 2. * Array, 2, 4, 8, 16..... */ TRANSIENT volatile Cell[] cells; /** * Base value, used mainly when there is no contention, But also as * a fallback during table initialization races. Updated via CAS. */ TRANSIENT Volatile long base; /** * Spinlock (locked via CAS) used when resizing and/or creating Cells. */ TRANSIENT volatile int cellsBusy;Copy the code

The basic idea of LongAdder is to disperse hot spots. The value is distributed into a Cell array. Different threads will hit different slots in the array. To get the real long value, you simply return the values of the variables in each slot.

Sun () will accumulate values and bases in all Cell arrays as return values. The core idea is to spread the update pressure of AtomicLong’s previous value into multiple values, thus reducing the update hotspot.

Sum =Base+∑inCell[I]Value=Base+ \sum_i^nCell[I]Value=Base+∑inCell[I]

LongAdder bug: If the sum has been modified by other threads, the result is not accurate enough

LongAdder, like AtomicLong, operates on the same base in the absence of competition. When competition occurs, it divides the whole into parts, replacing space with time, using an array of cells, and splitting a value into this array of cells.

When multiple threads need to operate on a value at the same time, they can hash the thread ID to obtain a hash value, map the hash value to a subscript of the array cells, and increment the value corresponding to the subscript.

When all threads are finished, all the values of the array cells and the uncontested value base are added up as the final result.