java Concurrent Programming - 12 atomic classes

Posted by gonsman on Sun, 08 Dec 2019 20:08:06 +0100

background

Multithreading updates the value of variables, which may not get the expected value. Of course, adding the synchronized keyword can solve the problem of thread concurrency.
Another solution is provided here, that is, the atomic operation class under the java.util.concurrent.atomic package, which provides a simple, efficient and thread safe way to update variables.

The other two additional classes take a look by default:

Long addr multithreaded sum operation
The performance of the function operation under the multithreading of the long emulator is lower than that of the AtomicLong, mainly because of the function support;

Simple classification:

Basic type atomic class

Update basic types using atoms, including:

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

Core approach:

Look directly at the source code.

Class signature:
public class AtomicInteger extends Number implements java.io.Serializable {}

Method Function description
Construction method Two construction methods, do not pass or pass in value
get method get() gets the value; the corresponding set(int) method, layzySet(int) lazy setting
getAndAdd(int) Get the old value and then add a number, corresponding to addAndGet(int), add a number and return the new value
getAndSet(int) Get old value and update to new value
getAndIncreament() Get the old value and then + 1, corresponding to incrementandget() + 1 and return the new value
getAndDecrement() Get the old value and then - 1, corresponding to incrementandget() - 1 and return the new value
getAndUpdate(IntUnaryOperator) Get the old value and execute a function to get the new value and set it. The corresponding function is updateAndGet(IntUnaryOperator). First execute the built-in function interface and then return the new value
getAndAccumulate(int,IntBinaryOperator) Get the old value, and then set the return value of the old value and the first parameter for function operation. The corresponding one is accumulaedandget (int, intbinaryoperator) to perform the operation and then return the new value
compareAndSet(int,int) If the comparison is equal to the expected value, it is set to a new value. The corresponding value is weakCompareAndSet(int,int), which is not guaranteed to be set in sequence
toString Returns the string form of a number
number inherited method longValue(),byteValue() does type conversion directly
Methods inherited from object Directly follow the method of Object

The bottom layer is implemented based on unsafe and atomicity based on CAS;

To study the implementation source code of unsafe:

    /**
     * Atomically decrements by one the current value.
     *
     * @return the previous value
     */
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

The general processing flow is: dead cycle, comparison; that is CAS;
Using the characteristics of volatile, multi-threaded variable visibility;

Atomic array

Update an element in the array by atom;

There are three classes:

  • AtomicIntegerArray
  • AtomicIntegerLongArray
  • AtomicReferenceArray

Grasp a class to analyze:

public class AtomicIntegerArray implements java.io.Serializable {}

Method Explain
Construction method public AtomicIntegerArray(int length), public AtomicIntegerArray(int[] array) a clone will be made here, which does not affect the value of the incoming array
length Get the length of the inner array
get,set,layziset Get, set, lazy settings
compareAndSet,weakCompareAndSet CAS operation, peak method does not guarantee the order of operation
getAndAdd,getAndUpdate,getAndAccumulate There is a reverse way, which is to calculate first and then return the new value
toString Print out the array [number 1, number 2]

The operation of atomic type is special:

    /**
     * Atomically adds the given value to the element at index {@code i}.
     *
     * @param i the index
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int i, int delta) {
        return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
    }
 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;
    }

A shift operation is used to get the values in the array;

Update reference

Atomic update class AtomicInterger can only update one variable. If you want to update multiple different variables, you need to update the class provided by the reference type;

  • AtomicReference update reference type
  • AtomicReferenceFieldUpdater updates fields of reference type
  • Atomicmarkablerenewal updates reference types with flags

Take AtomicReference as an example:

Signature: public class AtomicReference implements java.io.Serializable {}

Method:

Method Explain
Construction method public AtomicReference(V initialValue) with initial value; public AtomicReference()
get,set,lazySet Settings, get, lazy settings
compareAndSet,weakCompareAndSet CAS operation, the week method does not guarantee the order
getAndSet,getAndUpdate,getAndAccumulate Reverse operation
toString Print out the objects inside

Underlying analysis:

    /**
     * Atomically sets to the given value and returns the old value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    @SuppressWarnings("unchecked")
    public final V getAndSet(V newValue) {
        return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
    }

The characteristics of unsafe are used to ensure the atomic operation;

Atomic update field

To update a field of a class, you need to update the field class with atoms;

  • Atomicintegerfiledapdater needless to say, the inter field of the atomic update class
  • AtomicLongFieldUpdater needless to say, the atom updates the Long field of the class
  • AtomicStampedReference atom updates the reference type with version number. It can update the reference and the version number of the reference atom to solve the ABA problem;

Key points:

  1. Each time, you must use the static method newUpdater to create an updater and set classes and properties;
  2. The properties of the updated class must be decorated with public volatile;
package com.cocurrenttest.atomictest;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * Description: human entity
 * @author carter
 * Creation time: 19:27, December 6, 2019
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Person {

    private String name;

    //Note, it can only be int. integer will report an error
    public volatile int age;
}
package com.cocurrenttest.atomictest;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * Description: TODO
 * @author carter
 * Creation time: 19:26, December 6, 2019
 **/

public class TestAtomicIntegerUpdater {

    public static void main(String[] args) {

        final AtomicIntegerFieldUpdater<Person> personAtomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");

        Person person = Person.builder().name("lifuchun").age(30).build();

        personAtomicIntegerFieldUpdater.addAndGet(person,1);

        final int age = personAtomicIntegerFieldUpdater.get(person);
        System.out.println(age);

        assert age==31 : "Update failed";


    }

}

Summary

Atomic operation classes are introduced. For the appropriate scenarios, I'll briefly talk about the two scenarios I used:

  1. Reconciliation during multi task data copy to calculate the total number of modified lines or the total amount of migrated orders for comparison between the two sides;
  2. The local variable that needs to be passed in the lambda expression of flow operation is final, but the general type is not final in the method, and it needs to be modified in the middle process. The IDE prompts that atomic class can be used to wrap it, and then take final to modify it;

package com.cocurrenttest.atomictest;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * Description: TODO
 * @author carter
 * Creation time: 19:36, December 6, 2019
 **/

public class TestStream {

    public static void main(String[] args) {

        Integer age = 25 ;
      final String name="b";

      //some condition to change
      name ="bbb";

        final List<Person> personList = Arrays.asList(
                Person.builder().name("aaa").age(10).build(),
                Person.builder().name("bbb").age(20).build(),
                Person.builder().name("ccc").age(30).build()
        )
                .stream()
                .filter(item -> item.getAge() >= age)
                .filter(item->item.getName().contains(name))
                .collect(Collectors.toList());

        System.out.println(personList);

    }


    public static void main2(String[] args) {

        Integer age = 25 ;
      final AtomicReference<String> name=new AtomicReference<>("b");

        //some condition to change
        name.set("bbb");;

        final List<Person> personList = Arrays.asList(
                Person.builder().name("aaa").age(10).build(),
                Person.builder().name("bbb").age(20).build(),
                Person.builder().name("ccc").age(30).build()
        )
                .stream()
                .filter(item -> item.getAge() >= age)
                .filter(item->item.getName().contains(name.get()))
                .collect(Collectors.toList());

        System.out.println(personList);

    }

}

Original is not easy, reprint please indicate the source.

Topics: Java Lombok emulator Lambda