Java Concurrency --- atomic CAS

A method to achieve concurrent operation of two ways: one is to use a lock (Synchronized and Lock), another is the use of atomic operations (CAS)

Synchronized based locking mechanism may lead to blockage of the problem:

a. the blocked thread a high priority
b. do not get the lock thread releases the lock has been how to do?
c. a lot of competition consume cpu, while bringing deadlock or other security issues

Based on the above problem, a CAS atomic
a.CAS principle: the use of CAS instructions of modern processors are supported, this cycle command until it succeeds
b.CAS (Compare And Swap): Instruction on the level of guarantee that this is an atomic operation
c three operator associated with the CAS: a memory address V, a a desired value, a new value of B
basic ideas: If the value of the address and on the desired value of V a is equal to, give the address assigning new values V B, if not , does nothing.
(In (infinite loop) cycle of continuous operation CAS)

d.CAS will also have some questions:

1) ABA problem (A-> B-> A): When the first memory fetch address V, to take the value of A, it can be performed when the CAS operation before, has a value of A to B of the thread after then changed to a, the value of this time, although the same, but the content has been changed. (Example: In real life, you are drinking a glass of water on to my colleagues on the nest, again filling the cup in there obviously, although it is still filled with the cup of water, nature is entirely. Like a) solution: You can solve A1-> B2 ----> A3 (similarly optimistic locking, most of the data version (version based) mechanism implemented by recording a version number).

2) overhead problem: CAS long-term operation is not successful, cpu continuous cycle.

3) ensure that only a shared variable atomic operation

Using atomic operations class:
the JDK using atomic operations related classes:
1) Updated basic types: AtomicBoolean, AtomicInteger, AtomicLong
simple code sample (of AtomicInteger)

associated method is relatively simple: getAndIncrement () to the value 1 plus
incrementAndGet () first and then add the value 1

package 原子操作;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author lenovo
 */
public class UseAtomicInt {

    static AtomicInteger ai=new AtomicInteger(10);

    public static void main(String[] args) {
        //先取值再加1
        System.out.println(ai.getAndIncrement());
        //先加1再取值
        System.out.println(ai.incrementAndGet());
        System.out.println(ai.get());
    }
}

The results show:
Here Insert Picture Description
2) update the array class: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
simple code examples (AtomicIntegerArray)
when making changes to the value, in fact, is a copy of an array, and re-assignment, the value of the original array has not changed.

package 原子操作;

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * @author lenovo
 */
public class AtomicArray {

    static int []value=new int[]{1,2};

    static AtomicIntegerArray ai = new AtomicIntegerArray(value);

    public static void main(String[] args) {
        //第一个参数代表原数组的下标值 第二个参数代表值
        ai.getAndSet(0,3);
        System.out.println(ai.get(0));
        System.out.println(value[0]);

    }
}

The results are shown (a first value indicating the result after the change of the new array, the second result represents the value of the original array)
Here Insert Picture Description
3) update the reference type: AtomicReference, AtomicMarkableReference, AtomicStampedReference
reference type atomic operation classes encapsulate the plurality of data, a plurality of variables has been reached atomic operation
simple code sample (AtomicReference):
when UserInfo this type of packaging names and ages of these two variables, the changed value, is re-copied, and then change the value.

package 原子操作;

import java.util.concurrent.atomic.AtomicReference;

/**
 * @author lenovo
 * 类说明:演示引用类型的原子操作类
 */
public class UseAtomicReference {

    static AtomicReference<UserInfo> userRef=new AtomicReference<UserInfo>();

    public static void main(String[] args) {
        UserInfo user=new UserInfo("zs",15);
        userRef.set(user);

        UserInfo updateUser=new UserInfo("ls",17);
        //这是候的更改是在新生成得对象进行变更
        userRef.compareAndSet(user,updateUser);
        System.out.println(userRef.get().getName());
        System.out.println(userRef.get().getAge());
        System.out.println(user.getName());
        System.out.println(user.getAge());


    }

    static class UserInfo{
        private String name;
        private int age;

        public UserInfo(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }


    }
}


The results show (the first two values ​​represent reference values ​​after the change, the two values ​​represents the value of the original quoted):
Here Insert Picture Description

4) Update field class: AtomicReferenceFieldUpdater, AtomicIntegerFieldUpdater, AtomicLongFieldUpdater (rarely used)

解决ABA问题的两个类: AtomicMarkableReference 处理版本使用的是boolean (表示有没有动过)
AtomicStampedReference: 表示动过几次(它的构造函数为AtomicStampedReference(V initialRef, int initialStamp ) 第一个参数表示引用类型的值,第二参数表示版本号 )

简单代码示例(AtomicStampedReference具有较强的实用性,表示的是当我们遇到ABA问题时可采用版本号进行分辨后操作)
该代码中有两个线程对带有版本戳的类进行更改,其中一个线程是在版本正确的情况对引用类型的值进行更改(这种情况下是可以更改成功的)另外一个线程是在版本不正确的情况下对引用类型的值进行更改(这种情况下是更改失败的)
compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) 返回的结果值是Boolean表示更改成功或者失败
其中第一个参数表示期待的引用值,第二个表示新的引用值。
第三个参数表示的是期待的版本戳,第四个表示新的版本戳

package 原子操作;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author lenovo
 * 演示带版本戳的原子操作类
 */
public class UseAtomicStampedReference {

    //AtomicStampedReference(V initialRef, int initialStamp) 传初始值和版本号
    static AtomicStampedReference<String> asr=
            new AtomicStampedReference<String>("Mark",0);

    public static void main(String[] args) throws InterruptedException {
        final int oldStamp=asr.getStamp();
        final String oldReference=asr.getReference();

        System.out.println(oldReference+"======"+oldStamp);

        //正确版本号的线程
        Thread rightStampThread=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+
                        "当前变量值为:"+oldReference+" 当前版本号为:"+oldStamp+"-"
                        +asr.compareAndSet(oldReference,oldReference+" Java",
                        oldStamp,oldStamp+1));


            }
        });

        //错误版本号的线程
        Thread errorStampThread=new Thread(new Runnable() {
            @Override
            public void run() {

                System.out.println(Thread.currentThread().getName()+
                        "当前变量值为:"+asr.getReference()+" 当前版本号为:"+asr.getStamp()+"-"
                        +asr.compareAndSet(asr.getReference(),asr.getReference()+" C",
                        oldStamp,oldStamp+1));


            }
        });

        rightStampThread.start();
        rightStampThread.join();
        errorStampThread.start();
        errorStampThread.join();
        System.out.println(asr.getReference()+"======"+asr.getStamp());
    }
}

结果展示(第一行的值表示的是原本引用类型的值和版本号 第二行的值表示的是一个在正确版本戳下对引用类型值的改变
第三行的值表示的是在一个错误版本戳下对引用值类型的改变,第四行代表的更改后最终的结果值)
Here Insert Picture Description

发布了19 篇原创文章 · 获赞 2 · 访问量 418

Guess you like

Origin blog.csdn.net/TheWindOfSon/article/details/103863601