CAS操作号称无锁优化,也叫作自旋;对于一些常见的操作需要加锁,然后jdk就提供了一些以Atomic开头的类,这些类内部自动带了锁,当然这里的锁并非是用synchronized来实现的,而是通过CAS操作来实现的;
一、下面是 AtomicInteger 的使用:
package com.designmodal.design.juc01; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @author D-L * @Classname T03_AtomicInteger * @Version 1.0 * @Description 使用 AtomicInteger 类解决常见的 多线程count++ * 其内部使用了CAS操作来保证原子性 但是不能保证多个方法连续调用都是原子性 * @Date 2020/7/21 0:35 */ public class T03_AtomicInteger { //使用AtomicInteger类 AtomicInteger count = new AtomicInteger(0); public void m(){ for (int i = 0; i < 10000; i++) { //等同于 在 count++ 上加锁 count.incrementAndGet(); } } public static void main(String[] args) { T03_AtomicInteger t = new T03_AtomicInteger(); List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 10; i<div style="color:transparent">本文来源gaodai.ma#com搞#代!码(网</div>++) { threads.add(new Thread(t::m , "Thread" + i)); } threads.forEach((o) -> o.start()); threads.forEach(o ->{ try { o.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(t.count); } }
二、当然达到使用的级别很简单,看一下API就好了,通过上面的小程序,下面主要来聊一聊原理:
1、通过源码分析AtomicInteger
首先小程序中定义了一个 AtomicInteger 类型的变量count;
AtomicInteger count = new AtomicInteger(0); public void add(){ count.incrementAndGet(); }
调用了AtomicInteger类中incrementAndGet();
/** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
调用unsafe类中的 getAndAddInt(Object var1, long var2, int var4)方法;
public native int getIntVolatile(Object var1, long var2); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
这里通过以上三步的操作,最终会进入Unsafe类这里调用的 compareAndSwapInt 意思就是比较然后交换,通过一个while循环,在这里转呀转,直到修改成功;
CAS(compareAndSwap)(比较并交换):原来想改变的值为0 ,现在想修改成1 ,这里想做到线程安全就必须要加synchronized,现在想用另外一种方式来替换加锁的方法,就是所谓的CAS操作;你可以把它想象成拥有三个参数的方法cas(V , Expected , NewValue); 第一个参数V是你要改的那个值,Expected第二个参数是你期望当前的值是多少(也就是如果没有线程修改的时,这个值应该是多少,如果不是期望值那就证明有别的线程修改过),NewValue是要设置的新值;
上图简单模拟了CAS操作的过程,当线程1和线程2同时读取了共享变量count = 0;在线程1修改的过程中,线程2已经将count值修改为1,那么在线程1修改的时候发现Expected值和V已经匹配不上了,证明已经有线程快我一步将count值改了(可能这里并发量大的时候已经有n多个线程已经修改过了),怎么办呢?那我只能将我的期望值修改成V的值、newValue 在这基础上加1,然后继续在这自旋操作,直到修改成功,这就是自旋操作;