多线程—CAS

概念:CAS(Compare And Swap)是对一种处理器指令的称呼。

先看一段代码:

private static int count = 0;
public static void add(){
    count++;
}

如果多个线程调用add()方法,会有线程安全问题:

  • 第一,count静态变量的可见性问题。

  • 第二,count++操作的原型性问题。

那么如何解决呢?

第一种方案:将count++放在synchronized同步代码块里,原子性和可见性都可以解决,但是问题在于synchronized需要加锁,属于比较重量级的操作,影响性能。

代码:

private static int count = 0;
public static void add(){
    synchronized(this){
        count++;
    }
}

注意:使用volatile只能解决count静态变量的可见性问题,无法解决原子性问题,所以无法完全解决问题。

第二中解方案:创建一个AtomicInteger类,将count的初始值传入,然后调用AtomicInteger类的incrementAndGet()方法替换count++操作,incrementAndGet()方法的底层是使用的compareAndSet()方法,该方法的原理下面会讲到。

代码:

private static int count = 0;
public static void add(){
   	//创建一个原子类 
        AtomicInteger atomicInteger = new AtomicInteger(count);
        //调用原子类的自增方法
        int i = atomicInteger.incrementAndGet()
}

count++的线程安全问题主要体现在它不是原子性操作,是一种read-modify-write操作,这就导致了线程读写时候的问题。

第三种方案:创建一个AtomicInteger类,将count的初始值传入,然后使用compareAndSet()方法。

代码:

private static int count = 0;
private static AtomicInteger atomicInteger = new AtomicInteger(count);
public static void add(){
    while(true){
        int countPre = atomicInteger.get();
        int countNext = atomicInteger.get() + 1;
    	//主要是这个方法的使用compareAndSet(v1,v2)
        if(atomicInteger.compareAndSet(countPre,countNext)){
            break;
        }
    }
    	
}

compareAndSet(v1,v2),v1是改变之前的值,v2是改变之后的值。该实现方式主要是为了更好的理解compareAndSet()方法,因为相应的类和方法都是封装起来的,理解方法的原理,以便大家更好的使用相关类和方法。

方法原理:

第一步:首先获取v1的值,再获取v2的值。

第二部:当调用compareAndSet()方法时,会再次获取v1的值。

第三步:比较两次获取的v1值是否相等。相等就交换,并且compareAndSet()方法返回true,循环结束;不相等就继续循环,再回到第一步的操作,直到循环结束。

CAS也是有缺点的:

  • 两次v1的值不相等,就会循环,如果一直循环开销会很大。

  • 只能保证一个变量的原子操作。如果保证多个共享变量的原子性,需要使用到锁。

  • 经典ABA问题。

ABA问题:

  • 例如一个数据int类型的n=10,A线程将n改为20之后,B线程将n改回10,C线程读取时,n还是10,AB线程更改n的过程对于C线程来说就像没发生过一样。

CAS特点:

CAS和volatile配合使用可以实现无锁并发,适用于线程数少、多核cpu下。

  • CAS是基于乐观锁的思想。

    乐观锁:它假定当前环境写操作比读操作少,并且遇到并发的概率很低,在读数据的时候认为别的线程不会更改正在读的数据,在写数据时,判断当前获取的值和期望的值是否相同,如果相同则则进行更新,更新期间的操作时原子性的。

  • 而synchronized是基于悲观锁实现的,对每一个线程访问共享变量时,都觉得不安全,所以需要线程一个一个的访问。

敲黑板:

  • CAS主要思想体现在无锁并发,无阻塞并发。

    好好理解一下:

    1. CAS不会像synchronized一样,使得线程会阻塞。

    2. 如果CAS使用时,当前值与期望值一直无法相等,那么必然会一直循环重试,反而会影响性能。

你可能感兴趣的:(多线程,java,后端,多线程)