并发之CAS

    我们通常将多个操作变成一个原子操作的方式有:(1) synchronized (2)lock(3)CAS。这里主要来讲一下CAS这种机制,简单的说,CAS是一种轻量级锁,巧妙的使用比起一味的使用synchronized的性能好很多。
    我们先来说一下什么CAS,用一个例子形象的说明。我们在写程序的时候经常用到i++这样的操作,我们表面看是一行指令,其实到了底层并不是一个原子操作,会拆分成,读取i,i加1,写入i等步骤,也就是我们写i++,是线程不安全的,完全可能两个线程同时读取i,然后在加1,再写入,本来两个线程执行i++,效果是i+2,但由于不是原子操作,所以最终结果还是i+1。这个时候我们可能会考虑用synchronized了,但是synchronized是比较重量级的,所以这里我们可以是用CAS,那cas是什么呢,cas是从硬件层面引入的一种机制,实现对单个变量的修改为原子操作,至于怎么从硬件层面实现的,也可以去调查一下,这里不做赘述,我们只要知道,当我们对单个变量进行操作时,如果使用CAS,能直接实现原子操作,并且不用加锁,不加锁也就意味性能的提升。

 import java.util.concurrent.atomic.AtomicInteger;

public class CASTest {

    private static int count;

    public static void main(String[] args) {

//        test1();
        test2();
    }


    public static void test1(){
        AtomicInteger atomicInteger = new AtomicInteger(0);
        long time = System.currentTimeMillis();
        Thread thread1 = new Thread(()->{
            for(int i=0;i<100000;i++){
                atomicInteger.incrementAndGet();
//                synchronized (CASTest.class) {
//                    count++;
//                }
            }
        });
        Thread thread2 = new Thread(()->{
            for(int i=0;i<100000;i++){
                atomicInteger.incrementAndGet();
//                synchronized (CASTest.class) {
//                    count++;
//                }
            }
        });
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("i="+atomicInteger.get()+",消耗时间"+(System.currentTimeMillis()-time));
    }


    public static void test2(){
        long time = System.currentTimeMillis();
        Thread thread3 = new Thread(()->{
            for(int i=0;i<100000;i++){
                synchronized (CASTest.class) {
                    count++;
                }
            }
        });
        Thread thread4 = new Thread(()->{
            for(int i=0;i<100000;i++){
                synchronized (CASTest.class) {
                    count++;
                }
            }
        });
        thread3.start();
        thread4.start();
        try {
            thread3.join();
            thread4.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("i="+count+",消耗时间"+(System.currentTimeMillis()-time));
    }
}

输出

i=2000000,消耗时间123
i=2000000,消耗时间202

在大数据下,CAS比加锁有一个明显的性能提升。下面是通过代码和注释的方式讲一下CAS中经典问题:ABA问题,以及CAS家族中的成员,和使用方法:

package com.huifu.test.file;

import com.google.common.util.concurrent.AtomicDouble;
import com.google.common.util.concurrent.AtomicDoubleArray;

import java.util.concurrent.atomic.*;
import java.util.function.DoubleBinaryOperator;

public class CASABATest {

    public static void main(String[] args) {
//        test1();
//        test2();
        test3();
    }

    static AtomicInteger number = new AtomicInteger(0);


    public static void test1(){
        AtomicReference<Node> name = null;
        Node node1 = new Node("1",null);
        Node node2 = new Node("2",node1);
        Node node3 = new Node("3",node2);
        name = new AtomicReference<>(node3);
        System.out.println("子节点:"+name.get().getNext());

        //另一个线程
        Node oldNode = name.get();

        //AB
        Node node = name.get();
        Node newNode = new Node("4",null);
        System.out.println(name.compareAndSet(node,newNode));

        //BA
        node.setNext(null);
        System.out.println(name.compareAndSet(newNode,node));
        System.out.println("子节点:"+name.get().getNext());

        Node node4 = new Node("5",null);
        System.out.println(name.compareAndSet(oldNode,node4));
    }



    public static void test2(){
        AtomicStampedReference<Node> name = null;
        Node node1 = new Node("1",null);
        Node node2 = new Node("2",node1);
        Node node3 = new Node("3",node2);
        name = new AtomicStampedReference<>(node3,0);
        System.out.println("子节点:"+name.getReference().getNext());

        //另一个线程
        Node oldNode = name.getReference();

        //AB
        Node node = name.getReference();
        int version = name.getStamp();
        Node newNode = new Node("4",null);
        System.out.println(name.compareAndSet(node,newNode,version,version+1));

        //BA
        node.setNext(null);
        version = name.getStamp();
        System.out.println(name.compareAndSet(newNode,node,version,version+1));
        System.out.println("子节点:"+name.getReference().getNext());

        Node node4 = new Node("5",null);
//        version = name.getStamp();
        System.out.println(name.compareAndSet(oldNode,node4,version,version+1));


    }


    public static void test3(){
        //基本CAS
        AtomicInteger atomicInteger = new AtomicInteger();
        AtomicBoolean atomicBoolean = new AtomicBoolean();
        AtomicLong atomicLong = new AtomicLong();
        AtomicDouble atomicDouble = new AtomicDouble();
        AtomicReference<String> atomicReference = new AtomicReference<>();
        //允许带版本号的CAS
        AtomicStampedReference<String> atomicStampedReference = new AtomicStampedReference<>("sd",0);

        //数组CAS,进行必须指定长度,并且进行修改时也需要指定索引,即指定数组中的哪一个
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(2);
        AtomicLongArray atomicLongArray = new AtomicLongArray(3);
        AtomicDoubleArray atomicDoubleArray = new AtomicDoubleArray(4);
        AtomicReferenceArray<String> atomicReferenceArray = new AtomicReferenceArray<>(4);


        //修改器CAS,可以用于修改某一个对象的某个变量,该但是该变量必须是voliate修饰,并且可以直接访问(不能用private修饰)
        //用于修改类为Node的对象中变量名为val的字段,该字段为int类型
//        AtomicIntegerFieldUpdater<Node> atomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Node.class,"val");
        //用于修改类为Node的对象中变量名为val的字段,该字段为long类型
//        AtomicLongFieldUpdater<Node> atomicLongFieldUpdater = AtomicLongFieldUpdater.newUpdater(Node.class,"val");
        Node node = new Node("123",null);
        Node node1 = new Node("234",node);
        //用于修改类为Node的对象中变量名为val的字段,该字段为String类型
        AtomicReferenceFieldUpdater<Node,String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class,String.class,"val");
        atomicReferenceFieldUpdater.set(node1,"123456");
        System.out.println(node1.getVal());


        //jdk1.8以后,CSA家族中新增了DoubleAccumulator,DoubleAdder,LongAccumulator,LongAdder这四个类,效率比AtomicXXX更块,原因是这个采用了多车道模式,
        // 就是有多个缓存同时在累加,取值的时候,需要把每个车道的加起来,典型的时空间换时间,XXXAccumulator需要传入一个方法,来定义累计的方法,如下,XXXAdder只能加。
        DoubleAccumulator doubleAccumulator = new DoubleAccumulator(new DoubleBinaryOperator() {
            @Override
            public double applyAsDouble(double left, double right) {
                System.out.println("left="+left);
                System.out.println("right="+right);
                return left+right;
            }
        },100);
        doubleAccumulator.accumulate(40);
        System.out.println("doubleAccumulator="+doubleAccumulator.get());
        LongAccumulator longAccumulator = null;

        DoubleAdder doubleAdder = new DoubleAdder();
        doubleAdder.add(50);
        doubleAdder.add(40);
        System.out.println("doubleAdder="+doubleAdder.sum());
    }
}

猜你喜欢

转载自blog.csdn.net/qq_30095631/article/details/106001755