首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Java并发编程-CAS(1)

Java并发编程-CAS(1)

CAS,Compare and Swap即比较并替换,设计并发算法时常用到的一种技术,Doug lea大神在java同步器中大量使用了CAS技术,鬼斧神工的实现了多线程执行的安全性。
目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false。
实现分析先看一段代码


public int a = 1;public boolean compareAndSwapInt(int b) {    if (a == 1) {        a = b;        return true;    }    return false;}
试想这段代码在多线程并发下,会发生什么?我们不妨来分析一下:
  • 线程A执行到 a==1,正准备执行a = b时,线程B也正在运行a = b,并在线程A之前把a修改为2;最后线程A又把a修改成了3。结果就是两个线程同时修改了变量a,显然这种结果是无法符合预期的,无法确定a的值。
  • 解决方法也很简单,在compareAndSwapInt方法加锁同步,变成一个原子操作,同一时刻只有一个线程才能修改变量a。
CAS中的比较和替换是一组原子操作,不会被外部打断,先根据paramLong/paramLong1获取到内存当中当前的内存值V,在将内存值V和原值A作比较,要是相等就修改为要修改的值B,属于硬件级别的操作,效率比加锁操作高。
java.util.concurrent.atomic包下的原子操作类都是基于CAS实现的,接下去我们通过AtomicInteger来看看是如何通过CAS实现原子操作的:
public class AtomicInteger extends Number implements java.io.Serializable {    // setup to use Unsafe.compareAndSwapInt for updates    private static final Unsafe unsafe = Unsafe.getUnsafe();    private static final long valueOffset;    static {        try {            valueOffset = unsafe.objectFieldOffset                (AtomicInteger.class.getDeclaredField("value"));        } catch (Exception ex) { throw new Error(ex); }    }    private volatile int value;    public final int get() {return value;}}
  • Unsafe是CAS的核心类,Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的原子操作。
  • valueOffset表示的是变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的原值的。
  • value是用volatile修饰的,保证了多线程之间看到的value值是同一份。
接下去,我们看看AtomicInteger是如何实现并发下的累加操作:
//jdk1.8实现public final int getAndAdd(int delta) {        return unsafe.getAndAddInt(this, valueOffset, delta);}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;}
在jdk1.8中,比较和替换操作放在unsafe类中实现。
假设现在线程A和线程B同时执行getAndAdd操作:
  • AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,根据Java内存模型,线程A和线程B各自持有一份value的副本,值为3。
  • 线程A通过getIntVolatile(var1, var2)方法获取到value值3,线程切换,线程A挂起。
  • 线程B通过getIntVolatile(var1, var2)方法获取到value值3,并利用compareAndSwapInt方法比较内存值也为3,比较成功,修改内存值为2,线程切换,线程B挂起。
  • 线程A恢复,利用compareAndSwapInt方法比较,发手里的值3和内存值4不一致,此时value正在被另外一个线程修改,线程A不能修改value值。
  • 线程的compareAndSwapInt实现,循环判断,重新获取value值,因为value是volatile变量,所以线程对它的修改,线程A总是能够看到。线程A继续利用compareAndSwapInt进行比较并替换,直到compareAndSwapInt修改成功返回true。
返回列表