java.util.concurrent.atomic

Atomic manipulation

In a multi-threaded or concurrent environment, we often encounter this situation int i = 0; i ++ but this writing is not thread-safe. In order to achieve thread safety, we usually use synchronized to modify the corresponding code block, but because synchronized uses a pessimistic locking strategy, it is not a particularly efficient solution. Another way is to use the JUC package. atomic class.

The atomic package provides a series of simple operations, high performance, and thread-safe classes to update basic type variables, array elements, reference types, and update field types in objects. These classes under the atomic package all use the optimistic locking strategy to update the data atomically. In java, they are implemented using CAS operations.

case

CAS (O, V, A)
V: the actual variable value stored in the main memory
O: the current thread thinks the variable value of the main memory
A: the value that you want to replace the variable
main memory: num = 1;-> becomes 0 after passing the thread
Thread 1: num = 0; cas (1,1,0) O = V, think there is no thread to modify the main memory value at this time, update 0 to the main memory at this
timeThread 2: num = 0; cas (1,0 , 0) O! = V, think that some thread has modified this value, at this time the modification fails, return the latest value of main memory O, try again

volatile

The volatile modification variable is to synchronize the value of the variable between multiple threads in time. The so-called visibility refers to when a thread changes the value of a shared variable, and the new value is immediately known to other threads.

synchronized

Synchronized provides a lock mechanism that can ensure mutually exclusive access between shared variables, thereby preventing data inconsistency problems, while synchronized can ensure that a thread's changes are visible (visibility), that is, it can replace volatile.

Synchronized VS CAS

The main problem of the veteran-level Synchronized (before optimization) is: in the case of thread competition, there will be performance problems caused by thread blocking and wake locks, because this is a mutual exclusion synchronization (blocking synchronization). CAS is not an arbitrary thread suspend. When a CAS operation fails, a certain attempt will be made instead of a time-consuming suspend wakeup operation, so it is also called non-blocking synchronization. This is the main difference between the two

Why use the Atomic class

Although volatile is better than synchronized, it is a compound operation because operations such as i ++ do not conform to atomicity. We can simply say that this operation is understood to be composed of these three steps:
1. Read 2. Add 3. Assignment
So, in a multi-threaded environment, there may be threads that read num into local memory, at this time other threads may The num has been increased a lot, and the current thread still adds the expired num and rewrites it to the main memory, which eventually leads to the unexpected result of num.
So at this time you can use the atomic operation class in the java concurrent package. The atomic operation class guarantees its atomicity by circulating CAS.

Problems with CAS

1. ABA problem

Because CAS will check whether the old value has changed, there is such an interesting problem here. For example, an old value A changes to B, and then to A. Just when doing CAS, it was found that the old value did not change and remains A, but in fact it did change. The solution can follow the optimistic locking method commonly used in the database, adding a version number can be solved. The original change path A-> B-> A becomes 1A-> 2B-> 3C.

2. The spin time is too long

When using CAS, non-blocking synchronization means that the thread will not be suspended and will spin (it is nothing more than an infinite loop) for the next attempt. If the spin time is too long here, it will consume a lot of performance. If the JVM can support the pause instruction provided by the processor, there will be a certain improvement in efficiency.

Introduction to atomic tools

The atomic package improves the basic types of tools for atomic updates, mainly including:
1. AtomicBoolean: update boolean
by atomic update;
2. AtomicInteger: update Integer by atomic update ; 3. AtomicLong: update Long by atomic update ;
The usage of these classes is basically the same. Here we use AtomicInteger as an example to summarize the commonly used methods:

addAndGet(int delta) :以原子方式将输入的数值与实例中原本的值相加,并返回最后的结果;
incrementAndGet() :以原子的方式将实例中的原值进行加1操作,并返回最终相加后的结果;
getAndSet(int newValue):将实例中的值更新为新值,并返回旧值;
getAndIncrement():以原子的方式将实例中的原值加1,返回的是自增前的旧值;


AtomicIntegerArray : AtomicIntegerArray: atomic update elements in integer array;
2.AtomicLongArray: atomic update elements in long integer array;
3.AtomicReferenceArray: atomic update reference type array The elements in
these are used in the same way. Let's summarize the commonly used methods with AtomicIntegerArray:

addAndGet(int i, int delta):以原子更新的方式将数组中索引为i的元素与输入值相加;
getAndIncrement(int i):以原子更新的方式将数组中索引为i的元素自增加1;
compareAndSet(int i, int expect, int update):将数组中索引为i的位置的元素进行更新

For atomic update reference type variables, in order to ensure thread safety, atomic also provides related classes:
AtomicReference: atomic update reference type;
AtomicReferenceFieldUpdater: atomic update reference type field;
AtomicMarkableReference: atomic update reference type with mark bit;

If you need to update a certain field of the object, and in the case of multi-threading, to ensure thread safety, atomic also provides the corresponding atomic operation class:
AtomicIntegeFieldUpdater: atomic update integer field class;
AtomicLongFieldUpdater: atomic update long integer field Class;
AtomicStampedReference: Atomic update reference type, this update method will have a version number. And why it is accompanied by a version number when updating is to solve the CAS ABA problem;

To use atomic update fields requires two steps:

1. Atomic update field classes are all abstract classes, you can only create an updater through the static method newUpdater, and you need to set the class and attributes you want to update;
2. The attributes of the update class must be modified using public volatile;

public class AtomicDemo {
    private static AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
    public static void main(String[] args) {
        User user = new User("a", 1);
        int oldValue = updater.getAndAdd(user, 5);
        System.out.println(oldValue);
        System.out.println(updater.get(user));
    }

    static class User {
        private String userName;
        public volatile int age;

        public User(String userName, int age) {
            this.userName = userName;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
} 

Published 51 original articles · won praise 2 · Views 6374

Guess you like

Origin blog.csdn.net/wenwang3000/article/details/101049360