CAS
JUC中多数类是通过volatile和CAS来实现的,CAS本质上提供的是一种无锁方案,而Synchronized和Lock是互斥锁方案; java原子类本质上使用的是CAS,而CAS底层是通过Unsafe类实现的。
线程安全的方法有:
- 互斥同步:ReentrantLock 和 syschronized
- 非阻塞同步:CAS
- ThreadLocal
第一种方法是通过锁的方式来实现线程安全,CAS是通过无锁的方式实现的,下面重点说说CAS是如何实现线程安全的
什么是CAS
- JUC中多数类是通过volatile和CAS来实现的,CAS本质上提供的是一种无锁方案,而Synchronized和Lock是互斥锁方案; java原子类本质上使用的是CAS,而CAS底层是通过Unsafe类实现的
- CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。JDK中大量使用了CAS来更新数据而防止加锁(synchronized 重量级锁)来保持原子更新
就是有一个原值A, 如果要更新为值B,在更新前先比较原值A有没有发生变化,如果没有发生变化,就将A更新为值B,如果发生了变化就不交换
用AtomicInteger 举个小栗子:多线程情况下新增一个值
AtomicInteger a = new AtomicInteger(0); public int add(){ return a.addAndGet(1); }
CAS 有什么问题
synchronized是通过锁来保证线程安全的,是一种悲观锁策略
而CAS是一种乐观锁策略,但是在并发问题上性能更佳,它可能会出现的问题
- ABA问题:CAS需要在操作值的时候,检查值有没有发生变化,比如没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时则会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。比如数据表来说,可以加个version字段。不过在jdk1.5后,加了个AtomicStampedReference这个类,这个类就是利用方法compareAndSet来检查当前引用是否等于预期引用,还有标志stamp是否等于预期的标志stamp,如果都相等就更新
- CAS自旋时间太长,会导致CPU的大的执行开销
Unsafe类
我们上面提过,很多原子类是通过Unsafe实现的,
可以看一眼Unsafe类
public final class Unsafe { ...// 省略 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; } public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); return var6; } public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var4)); return var5; } public final long getAndSetLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var4)); return var6; } public final Object getAndSetObject(Object var1, long var2, Object var4) { Object var5; do { var5 = this.getObjectVolatile(var1, var2); } while(!this.compareAndSwapObject(var1, var2, var5, var4)); return var5; } ...//省略 }
可以发现,它是通过while自旋compareAndSwap进行CAS更新(如果失败就一直自旋)。里面的compareAndSwap* 都是底层的native方法
下面是Unsafe可以实现的功能(取自网络图)
比如,AtomicInteger就是通过Unsafe实现的原子性
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // 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); } } // volatile 值 private volatile int value; /** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; } ...//省略 public final int getAndSet(int newValue) { return unsafe.getAndSetInt(this, valueOffset, newValue); } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } ...//省略 }
可以看到,value用了volatile来修饰,保证了线程的可见性,同时使用CAS来保证更新时的原子性