CAS方法会传递三个参数,第一个参数V表示要更新的变量,第二个参数E表示期望值,第三个参数U表示更新后的值。更新的方式是,如果V==E,表示预期值和实际值相等,则将V修改成U并返回true,否则修改失败返回false。
在Java中的Unsafe类中提供了CAS方法,针对int类型变量的CAS方法定义如下。
//这是java的本地方法 public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);
- paramObject,表示当前的实例对象。
- paramLong,表示实例变量的内存地址偏移量
- paramInt1,表示期望值。
- paramInt2,表示要更新的值。
paramLong表示目标变量X在实例对象paramObject中内存地址偏移量。换句话说,在预期值paramInt1要和目标变量X进行比较是否相等的判断中,目标变量X的值就是通过该偏移量从内存中获取的。
CAS在AtomicInteger中的应用AtomicInteger是一个能够保证原子性的Integer对象,也就是说,对于i++类的 *** 作,可以使用AtomicInteger来保证原子性,使用方法如下。
AtomicInteger atomicInteger = new AtomicInteger(0); atomicInteger.getAndIncrement(); //累加 *** 作
getAndIncrement()是用来实现原子累加的方法,每调用一次会在原来值得基础上+1,这个过程采用了CAS机制来保证原子性。
public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }
其中,valueOffset表示AtomicInteger中的成员变量value在内存中的偏移量,后续会用它直接从内存中读取value属性当前的值。valueOffset用到了unsafe.objectFieldOffset()方法,获取value字段在AtomicInteger.calss中的偏移量。
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); } }
unsafe.getAndAddInt的定义代码
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt) { while (true) { int i = getIntVolatile(paramObject, paramLong); if (compareAndSwapInt(paramObject, paramLong, i, i + paramInt)) { return i; } } }
- “i= this.getIntVolatile(paramObject,paramLong);”表示根据value在对象o的偏移量来获得当前的值i。
- 使用compareAndSwapInt()方法实现比较和替换,如果value当前的值和i相等,说明数据没有被其他线程修改过,则把value修改成i+paramInt。
- 这里采用了循环来实现,因为compareAndSwapInt()方法执行失败,则说明存在线程竞争,但是当前的方法是进行原子累加,所以必须要保证成功,为达到这个目的,就只能不断的循环重试,直到修改成功返回。
整体来说,CAS就是一种基于乐观锁机制来保证多线程环境下共享变量修改的原子性的解决方案。前面分析的案例虽然是在Java中的应用场景,但是它本质上和synchronized同步锁中用到的CAS是相同的。
CAS实现自旋锁自旋锁就是当一个线程在抢占资源时,如果锁已经被其他线程获取,那么该线程将会循环不断地判断及尝试抢占资源,在这个过程中线程一直保持运行状态,不会造成上下文切换带来的性能损耗。但是自旋锁也有缺点,如果获得锁资源的线程一直没有释放,那么就会一直重试从而造成CPU资源浪费。因此,synchronized中会用到固定次数的自旋和自适应自旋。
实现自旋锁的方式
- 通过for(;;)循环不断的循环重试。
- 通过一个线程安全的 *** 作去尝试抢占资源,CAS是一个满足原子 *** 作的方法,它的返回值true/false可以很好 地判断当前线程竞争的结果。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)