The beauty of Java [from cainiao to expert drills]: The principle and Analysis of atomic packages, javaatomic

Source: Internet
Author: User

The beauty of Java [from cainiao to expert drills]: The principle and Analysis of atomic packages, javaatomic

Author: erqing

Personal site: zhangerqing.cn mail: xtfggef@gmail.com Weibo: http://weibo.com/xtfggef


Introduction to Atomic

The Atomic package is another java package specifically designed for thread security under Java. util. concurrent. It contains multiple Atomic operation classes. This package provides a group of atomic variable classes. Its basic feature is that in a multi-threaded environment, when multiple threads simultaneously execute the methods contained by these class instances, they are exclusive, that is, when a thread enters the method, when the command is executed, it will not be interrupted by other threads, and other threads will wait until the execution of this method is complete, just like the spin lock, the JVM selects another thread from the waiting queue. This is just a logical understanding. It is actually implemented by using hardware-related commands and does not block threads (or just blocks threads at the hardware level ). You can operate on basic data, basic data in the array, and basic data in the class. The atomic variable class is equivalent to a generalized volatile variable that supports atomic and conditional read-Modify-write operations. -- Reference @Chenzehe.


Traditional lock problems

Let's look at an example: Counter (Counter), using the convenient lock mechanism synchronized keyword in Java. The preliminary code is as follows:

class Counter {private int value;public synchronized int getValue() {return value;}public synchronized int increment() {return ++value;}public synchronized int decrement() {return --value;}} 

In fact, there is no problem in meeting basic requirements for such a lock mechanism, but sometimes our needs are not so simple. We need a more effective and flexible mechanism, the synchronized keyword is based on the blocking lock mechanism. That is to say, when a thread has a lock, other threads accessing the same resource need to wait until the thread releases the lock. There are some problems: first, what if the priority of the blocked thread is very high? What should I do if the thread that obtains the lock never releases the lock? (This is very bad ). In another case, if there are a large number of threads competing for resources, the CPU will spend a lot of time and resources to deal with these competitions (in fact, the main work of the CPU is not). At the same time, there may also be some situations such as deadlocks. In the end, the lock mechanism is actually a rough and granular mechanism, which is a little too heavy than the demand for counters. Therefore, we look forward to a more appropriate and efficient thread security mechanism for this requirement.


Hardware synchronization Policy

The current processors support multiple processing methods. Of course, multiple processors also share the peripheral devices and memory. At the same time, the instruction set is enhanced to support special requirements for multiple processing. In particular, almost all processors can block other processors to update shared variables.


Compare and swap (CAS)

The current processor basically supports CAS, but the algorithms implemented by each manufacturer are not the same. Each CAS operation process contains three operators: one memory address V, an expected value A and A new value B. During the operation, if the value stored on this address is equal to the expected value A, the value on the address is assigned to the new value B, otherwise, no operation is performed. The basic idea of CAS is to assign a new value to the address if the value on the address is equal to the expected value. Otherwise, nothing will be done, but the original value will be returned. Let's look at an example to explain the implementation process of CAS (not the actual implementation of CAS ):

class SimulatedCAS {private int value;public synchronized int getValue() {return value;}public synchronized int compareAndSwap(int expectedValue, int newValue) {int oldValue = value;if (value == expectedValue)value = newValue;return oldValue;}}

The following is a Counter implemented using CAS.

public class CasCounter {    private SimulatedCAS value;    public int getValue() {        return value.getValue();    }    public int increment() {        int oldValue = value.getValue();        while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)            oldValue = value.getValue();        return oldValue + 1;    }}

Before JDK5.0, it is impossible for the Atomic class to implement a lock-free and wait-free algorithm unless the local library is used, which is possible since the Atomic variable class is available. The following figure shows the class structure under the java. util. concurrent. atomic package.

  • Scalar class: AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference
  • Array class: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
  • Updater class: AtomicLongFieldUpdater, AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater
  • Composite variable class: AtomicMarkableReference, AtomicStampedReference

The first group of four basic types: AtomicBoolean, AtomicInteger, AtomicLong, and AtomicReference are used to process Boolean, integer, long integer, and object data. The internal implementation of these four types is not simply using synchronized, it is a more efficient CAS (compare and swap) + volatile and native method, which avoids the high-availability of synchronized and greatly improves the execution efficiency. Let's take a look at an example. The atomic operation corresponding to my usual I ++ is: getAndIncrement ()

public static void main(String[] args) {AtomicInteger ai = new AtomicInteger();System.out.println(ai);ai.getAndIncrement();System.out.println(ai);}

Let's take a look at the implementation of AtomicInteger:

    /**     * Atomically increments by one the current value.     *     * @return the previous value     */    public final int getAndIncrement() {        return unsafe.getAndAddInt(this, valueOffset, 1);    }

Here we call a class called Unsafe to process it. It seems that we still need to look at the source code of the unsafe class. In JDK 8, the UnSafe class under sun. misc is displayed. Click to view the source code.

According to source code annotations, this class is a set of methods used to perform low-level and insecure operations. Although this class and all the methods are public, the use of this class is still limited. You cannot directly use this class in your own java program, this is because only the trusted code can obtain instances of this class. Therefore, our usual code cannot use this class, because its design operations are too low-level. If the operation is accidental, it may cause a great disaster, so we directly prohibit access to common code, of course, JDK is no problem.


CAS in Atomic

According to the previous explanation, the CAS principle is to compare the expected value with the original one. If it is the same, it will be updated to the new value. How can this "Original Value" come from here, let's look at the implementation in AtomicInteger:

// 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 new Error(ex); }    }

Here we use the UnSafe method objectFieldOffset () to view the source code:

/**     * Report the location of a given static field, in conjunction with {@link     * #staticFieldBase}.     * <p>Do not expect to perform any sort of arithmetic on this offset;     * it is just a cookie which is passed to the unsafe heap memory accessors.     *     * <p>Any given field will always have the same offset, and no two distinct     * fields of the same class will ever have the same offset.     *     * <p>As of 1.4.1, offsets for fields are represented as long values,     * although the Sun JVM does not use the most significant 32 bits.     * It is hard to imagine a JVM technology which needs more than     * a few bits to encode an offset within a non-array object,     * However, for consistency with other methods in this class,     * this method reports its result as a long value.     * @see #getInt(Object, long)     */    public native long objectFieldOffset(Field f);

This method is used to get the memory address of the "original value" we mentioned above. Is a local method, and the return value is valueOffset. Its parameter field is the value attribute defined in AtomicInteger:

    private volatile int value;    /**     * Creates a new AtomicInteger with the given initial value.     *     * @param initialValue the initial value     */    public AtomicInteger(int initialValue) {        value = initialValue;    }    /**     * Creates a new AtomicInteger with initial value {@code 0}.     */    public AtomicInteger() {    }

Value is a volatile variable. It can be seen in the memory that no thread can copy it. Therefore, JVM can ensure that any thread can always get the latest value of this variable at any time. Here, the value of the value can be passed in when the AtomicInteger class is initialized, or it can be left blank. If it is left blank, the value is automatically assigned to 0.


Let's go back to CAS and see how the getAndIncrement () method is implemented using CAS.

 /**     * Atomically increments by one the current value.     *     * @return the previous value     */    public final int getAndIncrement() {        return unsafe.getAndAddInt(this, valueOffset, 1);    }

Continue:

public final int getAndAddInt(Object o, long offset, int delta) {        int v;        do {            v = getIntVolatile(o, offset);//------------0---------------        } while (!compareAndSwapInt(o, offset, v, v + delta));//-------------1-------------        return v;    }

  /**     * Atomically update Java variable to <tt>x</tt> if it is currently     * holding <tt>expected</tt>.     * @return <tt>true</tt> if successful     */    public final native boolean compareAndSwapInt(Object o, long offset,//---------------2--------------                                                  int expected,                                                  int x);

I will explain it a bit. In fact, the comments of compareAndSwapInt are very clear. The Atomic variable value is updated to x. If true is returned successfully, we know that, if we do not input parameters when creating the AtomicInteger instance, the value of the original variable is 0, so the value of v obtained at // ---------- 0 ----------- is the code at 0 and 1:

While (! CompareAndSwapInt (o, offset, 0, 1) We know that the value corresponding to the address pointed to by offset is the initial value 0 of the original variable, so it is the same as the expected value 0, therefore, if the initial value is 1, true is returned. If the inverse value is false, the loop ends. If the return value is v, the value 0 before the update is returned. this is the implementation of atomic operations similar to I ++ operations. Of course, the final implementation of CAS is native, which is implemented in C language. We cannot see the source code here, I will decompile the code later.


CAS thread security

After talking about it for half a day, we will return to the original question: how can we achieve thread security? Please consider this issue first. In fact, we have not performed any synchronization operations at the language level. You can also see that the source code has no lock applied to it, but why is it thread-safe? This is the mystery of these classes under the Atomic package: the language layer does not handle it. We will hand it over to the hardware-CPU and memory, and use the multi-processing capability of the CPU to implement hardware-level blocking, coupled with the volatile variable feature, thread security based on atomic operations can be achieved. Therefore, CAS is not non-blocking, but blocking is not in terms of language and thread, but in terms of hardware. Therefore, such operations will undoubtedly be faster and more efficient!


Summary

Although the CAS-based thread security mechanism is very efficient, it should be noted that not all thread security can be implemented using this method, which is only suitable for a small granularity, type, such as the counter, is valid only when used, otherwise there will be no locks.


If you have any questions, please feel free to contact me. I will reply in time and welcome to discuss them together!

Author: Two green personal site: zhangerqing.cn mailbox: xtfggef@gmail.com Weibo: http://weibo.com/xtfggef

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.