Java Atomic summary

The so-called Atomic, translates atoms. Atom is considered to be the smallest unit of the operation, if it is a code atoms, then the code during execution, executing either succeed or fail. Atomic operations are generally achieved by the underlying CPU instruction. And these classes at atomic packets, you can let us in a multithreaded environment, is achieved by means of a lock-free thread-safe atomic operations.

class in packet basically by atomic Unsafe class, encapsulated by the CAS operation is achieved. Unsafe standard Java class does not belong, or after this class is a Java reserved category, in the JDK, on enhancing the performance of concurrent operations and the like, or NIO, mostly by means of this class to the packaging operations.
Java is a kind of compiled languages like C language can support the operation of memory, the memory is carried out by the JVM to create a recycling operation under normal circumstances, but this class provides direct access to the memory associated with the underlying operating so that we can also manually operating memory, but you can see the name of the class, this class is not safe, the official is not to suggest that we use.

CAS principle

3 parameters CAS CAS (V, E, N). V represents a variable to be updated, E represents an expected value, N represents the new value.

Only when the value is equal to V E value, will set the value of V N, if the value of V and E values ​​are different, then the other thread has been updated, the current thread does nothing. Finally, CAS returns the current the true value of V. CAS operation is performed holding optimistic attitude, it always believes he can successfully complete the operation.

When multiple threads simultaneously using a CAS operating variables, only one will win, and successfully updated, and the rest will fail. Fail thread will not be suspended, only to be told that failed and allowed to try again, of course, allowed to fail thread abort the operation. based on this principle, CAS no immediate operation lock, other threads can also be found to interfere with the current thread and process it properly.

In the atomic JDK8 packet, about 16 categories, according to the update mode atoms, it can be divided into four categories: atomic updates conventional type , atomic update array , atomic update references , atomic update field .

A common type atomic updates

Providing three basic types of atoms at atomic update package, respectively AtomicBoolean, AtomicInteger, AtomicLong, these atoms corresponding to the base class of Boolean type, shaping, long integer, as in Java other basic types, such as float, etc., If necessary, refer to these classes implement their own source.

AtomicBoolean

Main Interface

public final boolean get();
public final boolean compareAndSet(boolean expect, boolean update);
public boolean weakCompareAndSet(boolean expect, boolean update);
public final void set(boolean newValue);
public final void lazySet(boolean newValue);
public final boolean getAndSet(boolean newValue);复制代码

There are normal operation, it is mainly used in the CAS. A method much in this class, are substantially described above, the internal calculations are first converted to digital Boolean 0/1, then the subsequent calculation.

AtomicLong

Main Interface

public final long get();
public final void set(long newValue);
public final void lazySet(long newValue);
public final long getAndSet(long newValue);
public final boolean compareAndSet(long expect, long update);
public final boolean weakCompareAndSet(long expect, long update);
public final long getAndIncrement();
public final long getAndDecrement();
public final long getAndAdd(long delta);
public final long incrementAndGet();
public final long decrementAndGet();
public final long addAndGet(long delta);
public final long getAndUpdate(LongUnaryOperator updateFunction);
public final long updateAndGet(LongUnaryOperator updateFunction);复制代码

The next point and AtomicInteger similar to the following particular case.

AtomicInteger

Main Interface

// 取得当前值
public final int get();
// 设置当前值
public final void set(int newValue);
// 设置新值,并返回旧值
public final int getAndSet(int newValue);
// 如果当前值为expect,则设置为u
public final boolean compareAndSet(int expect, int u);
// 当前值加1,返回旧值
public final int getAndIncrement();
// 当前值减1,返回旧值
public final int getAndDecrement();
// 当前值增加delta,返回旧值
public final int getAndAdd(int delta);
// 当前值加1,返回新值
public final int incrementAndGet();
// 当前值减1,返回新值
public final int decrementAndGet();
// 当前值增加delta,返回新值
public final int addAndGet(int delta);复制代码

achieve

// 封装了一个int对其加减
 private volatile int value;
 .......
 public final boolean compareAndSet(int expect, int update) {
 // 通过unsafe 基于CPU的CAS指令来实现, 可以认为无阻塞.
 return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
 }
 .......
 public final int getAndIncrement() {
 for (;;) {
 // 当前值
 int current = get();
 // 预期值
 int next = current + 1;
 if (compareAndSet(current, next)) {
 // 如果加成功了, 则返回当前值
 return current;
 }
 // 如果加失败了, 说明其他线程已经修改了数据, 与期望不相符,
 // 则继续无限循环, 直到成功. 这种乐观锁, 理论上只要等两三个时钟周期就可以设值成功
 // 相比于直接通过synchronized独占锁的方式操作int, 要大大节约等待时间.
 }
 }复制代码

A simple example of a test case:

AtomicInteger atomicInteger = new AtomicInteger(1);
System.out.println(atomicInteger.incrementAndGet()); // 2
System.out.println(atomicInteger.getAndIncrement()); // 2
System.out.println(atomicInteger.getAndAccumulate(2, (i, j) -> i + j)); // 3
System.out.println(atomicInteger.get()); // 5
System.out.println(atomicInteger.addAndGet(5)); 复制代码

An array of atomic updates

Providing an array of the atomic packet three types of related updates atoms, respectively AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray, corresponding to integer, long integer, a reference type, the point is to note here that the updating means updates the array a an operating element.

And updating method is the same basic type of method, there are several methods simply look AtomicIntegerArray this class, similar to the other methods.

AtomicIntegerArray

Main Interface

// 获得数组第i个下标的元素
public final int get(int i);
// 获得数组的长度
public final int length();
// 将数组第i个下标设置为newValue,并返回旧的值
public final int getAndSet(int i, int newValue);
// 进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true
public final boolean compareAndSet(int i, int expect, int update);
// 将第i个下标的元素加1
public final int getAndIncrement(int i);
// 将第i个下标的元素减1
public final int getAndDecrement(int i);
// 将第i个下标的元素增加delta(delta可以是负数)
public final int getAndAdd(int i, int delta);复制代码

achieve

// 数组本身基地址
 private static final int base = unsafe.arrayBaseOffset(int[].class);

 // 封装了一个数组
 private final int[] array;

 static {
 // 数组中对象的宽度, int类型, 4个字节, scale = 4;
 int scale = unsafe.arrayIndexScale(int[].class);
 if ((scale & (scale - 1)) != 0)
 throw new Error("data type scale not a power of two");
 // 前导0 : 一个数字转为二进制后, 他前面0的个数
 // 对于4来讲, 他就是00000000 00000000 00000000 00000100, 他的前导0 就是29
 // 所以shift = 2
 shift = 31 - Integer.numberOfLeadingZeros(scale);
 }

 // 获取第i个元素
 public final int get(int i) {
 return getRaw(checkedByteOffset(i));
 }

 // 第i个元素, 在数组中的偏移量是多少
 private long checkedByteOffset(int i) {
 if (i < 0 || i >= array.length)
 throw new IndexOutOfBoundsException("index " + i);

 return byteOffset(i);
 }

 // base : 数组基地址, i << shift, 其实就是i * 4, 因为这边是int array.
 private static long byteOffset(int i) {
 // i * 4 + base
 return ((long) i << shift) + base;
 }

 // 根据偏移量从数组中获取数据
 private int getRaw(long offset) {
 return unsafe.getIntVolatile(array, offset);
 }复制代码

Use a simple example to test:

AtomicIntegerArray array = new AtomicIntegerArray(5);
array.set(0, 1); // 设置数组第一个值为1
System.out.println(array.getAndDecrement(0)); // 1
System.out.println(array.addAndGet(0, 5)); // 5复制代码

Atomic updates citation

Update the reference type atom class contains AtomicReference, (updated reference type), AtomicReferenceFieldUpdater (abstract class, update the reference in the type field), An AtomicMarkableReference (updated with a labeled reference type) three classes, these classes can be updated simultaneously multiple variables.

AtomicReference

AtomicInteger with similar, but which encapsulates an object, rather than int, a reference to be modified.

Main Interface

public final V get();

public final void set(V newValue);

public final boolean compareAndSet(V expect, V update);

public final V getAndSet(V newValue);复制代码

Test
using 10 threads simultaneously attempt to modify the AtomicReference String, ultimately only one thread can be successful.

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {
 public final static AtomicReference<String> attxnicStr = new AtomicReference<String>("abc");

 public static void main(String[] args) {
 for (int i = 0; i < 10; i++) {
 new Thread() {
 public void run() {
 try {
 Thread.sleep(Math.abs((int) (Math.random() * 100)));
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 if (attxnicStr.compareAndSet("abc", "def")) {
 System.out.println("Thread:" + Thread.currentThread().getId() + " change value to " + attxnicStr.get());
 } else {
 System.out.println("Thread:" + Thread.currentThread().getId() + " change failed!");
 }
 }
 }.start();
 }
 }
}复制代码

Atomic Update Field

If the update is only updated when the object of a certain field, you can use the field type atomic update package provides: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater and AtomicStampedReference, the first two as the name implies, is to update and long int type, the last one is updated reference type, the class provides a version number, be used to solve the problem ABA atomic update process may occur by CAS.
And the two previous classes AtomicReferenceFieldUpdater somewhat similar to the above described, are abstract classes need to be instantiated by newUpdater method and requirements for the field is the same.

AtomicStampedReference

ABA problem

A thread is ready to replace the value of variable B from A with CAS, the value of the two variable before the thread is replaced by a C A, C in turn replaced with three threads A, and then found that a thread variable value is still executing CAS it is a, so a thread CAS success.

Main Interface

// 比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳
public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp)
// 获得当前对象引用
public V getReference()
// 获得当前时间戳
public int getStamp()
// 设置当前对象引用和时间戳
public void set(V newReference, int newStamp)复制代码

analysis

// 内部封装了一个Pair对象, 每次对对象操作的时候, stamp + 1
 private static class Pair<T> {
 final T reference;
 final int stamp;
 private Pair(T reference, int stamp) {
 this.reference = reference;
 this.stamp = stamp;
 }
 static <T> Pair<T> of(T reference, int stamp) {
 return new Pair<T>(reference, stamp);
 }
 }

 private volatile Pair<V> pair;

 // 进行cas操作的时候, 会对比stamp的值
 public boolean compareAndSet(V expectedReference,
 V newReference,
 int expectedStamp,
 int newStamp) {
 Pair<V> current = pair;
 return
 expectedReference == current.reference &&
 expectedStamp == current.stamp &&
 ((newReference == current.reference &&
 newStamp == current.stamp) ||
 casPair(current, Pair.of(newReference, newStamp)));
 }复制代码

test

Requirements: Background users use multiple threads to recharge, recharge required only once.

public class AtomicStampedReferenceDemo {
 static AtomicStampedReference<Integer> money=new AtomicStampedReference<Integer>(19,0);
 public staticvoid main(String[] args) {
 //模拟多个线程同时更新后台数据库,为用户充值
 for(int i = 0 ; i < 3 ; i++) {
 final int timestamp=money.getStamp();
 newThread() { 
 public void run() { 
 while(true){
 while(true){
 Integerm=money.getReference();
 if(m<20){
 if(money.compareAndSet(m,m+20,timestamp,timestamp+1)){
                 System.out.println("余额小于20元,充值成功,余额:"+money.getReference()+"元");
 break;
 }
 }else{
 //System.out.println("余额大于20元,无需充值");
 break ;
 }
 }
 }
 } 
 }.start();
 }
 
 //用户消费线程,模拟消费行为
 new Thread() { 
 publicvoid run() { 
 for(int i=0;i<100;i++){
 while(true){
 int timestamp=money.getStamp();
 Integer m=money.getReference();
 if(m>10){
 System.out.println("大于10元");
   if(money.compareAndSet(m, m-10,timestamp,timestamp+1)){
        System.out.println("成功消费10元,余额:"+money.getReference());
 break;
 }
 }else{
 System.out.println("没有足够的金额");
 break;
 }
 }
 try {Thread.sleep(100);} catch (InterruptedException e) {}
 }
 } 
 }.start(); 
 }
 }复制代码

AtomicIntegerFieldUpdater

Can make ordinary variable can be atomic operations.

Main Interface

public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
 String fieldName);

public int incrementAndGet(T obj);复制代码
  • Updater can only modify variables that it is visible range. Because Updater reflection obtained using this variable. If the variable is not visible, an error occurs. For example, if the score is declared private, it is not feasible.
  • In order to ensure that the variable is correctly read, it must be volatile type. If our original code unstated this type, then simply have to declare it on the line.
  • Since the CAS operation will be assigned directly by the offset of the object instance, and therefore, it does not support static fields (Unsafe.objectFieldOffset () does not support static variables).

test

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterDemo {
 public static class Candidate {
 int id;
 // 如果直接把int改成atomicinteger, 可能对代码破坏比较大
 // 因此使用AtomicIntegerFieldUpdater对score进行封装
 volatile int score;
 }

 // 通过反射实现
 public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
 // 检查Updater是否工作正确, allScore的结果应该跟score一致
 public static AtomicInteger allScore = new AtomicInteger(0);

 public static void main(String[] args) throws InterruptedException {
 final Candidate stu = new Candidate();
 Thread[] t = new Thread[10000];
 for (int i = 0; i < 10000; i++) {
 t[i] = new Thread() {
 public void run() {
 if (Math.random() > 0.4) {
 scoreUpdater.incrementAndGet(stu);
 allScore.incrementAndGet();
 }
 }
 };
 t[i].start();
 }
 for (int i = 0; i < 10000; i++) {
 t[i].join();
 }

 System.out.println("score=" + stu.score);
 System.out.println("allScore=" + allScore);
 }
}复制代码

Type introduced after JDK8

These classes before JDK8, for atomic operations, we can substantially be provided by the above operation in the atoms completing our multi-threaded, but at high concurrency, these single performance of the above operation will spin CAS + is a problem, so these lower classes are generally used for concurrent operations.
And for this problem, JDK8 and the introduction of the following several categories: DoubleAdder, LongAdder, DoubleAccumulator, LongAccumulator , these classes is AtomicLong improvement and enhancement of these classes, these classes inherit from Striped64 this class.




Guess you like

Origin juejin.im/post/5de12a676fb9a071687e835c