Concurrent java - atomic (atomic operation) understanding

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/weixin_35959554/article/details/86232919

In java concurrency is a commonplace thing, in my understanding concurrency is the case of guarantee thread-safe, the same operation as the resources of multiple threads , depending on whether it is complicated by the three kinds of characteristics  :

  • Atomicity: a operation is not interrupted. Even when performed with a plurality of threads, one operation once started, will not interfere with other threads.
  • Ordering: sequential execution program that is orderly performed in the order code.
  • Visibility: When multiple threads access the same variable, a thread changes the value of this variable, other threads can immediately see the modified values. 

But the general did not synchronize multiple threads is unsafe . For example, an example of the self-energizing function of type int i ++;

        int count = 0;
    private void test() {
      //创建10个子线程
        List<Thread> threads = new ArrayList<Thread>();
        for (int j = 0; j < 100; j++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        count++;
                    }
                }
            });
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
       
        for (Thread thread : threads) {
            try {
                // 等待所有子线程执行完成 才运行主线程显示
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
          Log.e("count",count+"");

    }

According to the normal logic that should create 100 threads and each thread of the i operating, the end result should be 100,000, but after the test you will find that this is not the result, and the results are different every time.

The reason for this is that it is a thread-unsafe because of insecurity is the member variable i may occur non-atomic operations, different threads at the same time changing the value of this variable, resulting in a variable error occurred.

To avoid this we can operate atoms, in java can lock and cyclic CAS to implement atomic operation mode.

CAS cycle implemented using atomic operations

Introduction:

  • CAS (Compare And Swap) and compare Alternatively, a technique is used in the thread run concurrently
  • CAS is an atomic operation, to ensure concurrency-safe, and can not guarantee the synchronization of concurrent
  • CAS is a CPU instruction (JNI need to call Native method, CPU instructions to call)
  • CAS is a non-blocking, lightweight optimistic locking

 

CAS advantages:

  1. Non-blocking lightweight optimistic locking, by CPU instructions to achieve high performance in resource-less competitive situation, eliminating the overhead of locks to increase the throughput of the system

CAS Disadvantages:

  1. Long cycle time spending big. If the time is not successful spin CAS, it will bring a very large CPU execution cost.
  2. Atomic operation can only guarantee a shared variable. When performing operations on a shared variable, we can use the CAS cycle approach to ensure an atomic operation, but when multiple shared variables operating cycle CAS can not guarantee atomic operations
  3. ABA problem (probability is very small). Because the CAS needs to check the value at the time of operation under values ​​are not changed, if not changed is updated, but if a value is A, became a B, he is a A, you will find that when using CAS inspection its value has not changed, but actually changed. 

Substantially above that for the updated type int, the easiest method is to use atomic variables, JDK 1.5 is started to provide a class of atomic operations java.util.concurrent.atomic bag are applied to four kinds of atom types:

  1. Atomic update basic types
  2. An array of atomic updates
  3. Atomic updates citation
  4. Atomic update attributes (fields)

Operating categories:

  1. AtomicBoolean: atomic update Boolean
  2. AtomicInteger: integer atomic updates
  3. AtomicLong: atomic update Long

Here we are with the incrementAndGet AtomicInteger () method int add the number 1 ,.

//    static int count = 0;
    static AtomicInteger atomicInteger;
    private void test() {
        atomicInteger = new AtomicInteger(0);
        List<Thread> threads = new ArrayList<Thread>();
        for (int j = 0; j < 100; j++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        //     count++;
                        //  以原子方式将当前值加 1。
                        //addCount()

                       atomicInteger.incrementAndGet();
                    }
                }
            });
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            try {
                // 等待所有子线程执行完成 才运主线程
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//          Log.e("count",count+"");
        Log.e("count", atomicInteger + "");
    }

We run five times as follows:

We can see that every output is 100,000 in line with our pre-concurrent effect, which would address the atomic int's. In fact, it is used internally Unsafe class CAS operation to guarantee atomicity.

Let me give you an example:

    //使用CAS 实现自增
private void addCount() {
        for (;;) {
            int count = atomicInteger.get();
            boolean b = atomicInteger.compareAndSet(count , ++count );
            if (b) {
                break;
            }
        }
    }

This is an infinite loop, continue to get the current value, and the constant value assigned to own more than one greater self, if successful jump, will not succeed continuous loop until it is successful, the reason is that the assignment is unsuccessful when other threads also modify the value of this variable. Of course, such an operation is internal source.

    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return U.compareAndSwapInt(this, VALUE, expect, update);
    }

Source used was compareAndSwapInt () method of class CAS Unsafe operation.

Using locks (Synchronized) atomicity operations:

Each object in Java can be used as a lock (dependent on the JVM)
1. For normal synchronization method, the lock is the current instance of the object
2. For synchronous static method, the lock is an object of the current class Class 
3. For the block synchronization method, the lock is Synchronized the configuration of the object in parentheses 

Supplementary : For synchronized method or synchronized block of code, when an exception occurs, JVM will automatically release the lock is currently occupied by a thread, it will not lead to a deadlock due to abnormal phenomenon.

Remarks :

  • Synchronization method and static synchronized method relies on the way to achieve ACC_SYNCHRONIZED modifiers. JVM implementation of the method according to the synchronization modifier. When the method is called, the call instruction will ACC_SYNCHRONIZED access method to check whether the flag is set, if set, execution thread will first obtain the monitor, get to perform the method body after successfully executing the method after their release monitor. During method execution, no other thread can no longer obtain the same monitor object.
  • Using the sync block monitorenter and monitorexit instructions is implemented, it will go through the acquire and release locks in the region of the listener object sync block, thereby to control the synchronization byte code level scope.
 private void test() {
        List<Thread> threads = new ArrayList<Thread>();
        for (int j = 0; j < 100; j++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        addCount();
                    }
                }
            });
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            try {
                // 等待所有子线程执行完成 才运主线程
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
          Log.e("count",count+"");
    }
//上锁
    private synchronized  void addCount() {
        count++;
    }

I used the above code is locked Synchronized addCount () this method so that only one thread at the same time its operation to ensure atomicity.

Output results are as follows:

summary

Both methods Synchronized atomic operations and CAS, locked in java concurrency is also a veteran, and when using a thread locking their resources so that other threads can not access the resource, and can be accessed after unlocking until . For atomic shaping, long integer, and Boolean variables or deletion operation can AtomicInteger, AtomicBoolean AtomicLong and other types of atomic operations to achieve, when it is desired to ensure that objects of other types of atoms may also be provided by other classes Unsafe atomic statement, this can provide better performance than any other in the thread-safe. Of course, synchronized java is provided convenient and easy performance and good things, use is also common, so we can be used; CAS it, if the number of threads is too large, multiple threads are calling the cycle CAS interface, though not let obstruction caused by a thread, but cpu consume too serious, so the performance is better synchronized.

Guess you like

Origin blog.csdn.net/weixin_35959554/article/details/86232919