CAS(Compare and Swap)是一种并发机制,用于实现原子性操作。在并发编程中,当多个线程同时对共享变量进行操作时,会产生竞争条件(Race Condition),导致数据的不一致性、丢失、覆盖等问题。CAS机制通过比较期望值与实际值的方式,来确保正确性与一致性。
CAS的原理
CAS操作包括三个操作数:内存位置(V)、预期原值(A)和新值(B)。当且仅当内存位置上的值等于预期原值时,才会将该位置上的值修改为新值,否则不做任何操作。CAS操作是原子的,当线程A在进行CAS操作时,其它线程要么等待它执行完成,要么什么也不做。
Java中的CAS机制是由一组sun.misc.Unsafe类的方法实现的。该类是Java虚拟机内部的类,不在JDK标准包中,使用Unsafe中的方法需要调用反射机制。下面是一个使用Unsafe类的示例:
public class Counter {
private volatile int count;
public Counter(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void increment() {
// 调用CAS操作
boolean success = unsafe.compareAndSwapInt(this, offset, count, count + 1);
if (success) {
// CAS操作成功
count++;
} else {
// CAS操作失败,采用循环重试
increment();
}
}
private static final Unsafe unsafe;
private static final long offset;
static {
try {
unsafe = Unsafe.getUnsafe();
// 获取变量count在对象Counter中的偏移量
offset = unsafe.objectFieldOffset(Counter.class.getDeclaredField("count"));
} catch (NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
在上述示例中,使用volatile修饰的count变量具有原子性,但多线程并发访问count变量时,仍可能会发生竞争条件,导致数据不一致。因此,在increment方法中,使用CAS操作将count变量的值加1,保证了操作的原子性和一致性。
CAS的优缺点
CAS机制的优点在于它比使用锁的机制更容易适应高并发环境,因为线程不会被阻塞,而是不断进行重试,直到成功为止。另外,由于CAS操作是在用户态进行的,而不是内核态,因此开销较小,能够提高程序的执行效率。
然而,CAS机制也有缺点。首先,由于CAS机制依赖于硬件的支持,因此并不能在所有的计算机系统上都有效地实现。其次,在并发更新的情况下,由于多个线程都会尝试去修改同一个内存位置,会产生ABA问题,需要手动进行处理。
示例说明
下面是两条示例说明,分别是:
- 使用CAS实现一个简单的计数器。
public class CasCounter {
private volatile int count;
public int getCount() {
return count;
}
public void increment() {
int expectedValue;
int newValue;
do {
expectedValue = count;
newValue = expectedValue + 1;
} while (!compareAndSwap(expectedValue, newValue));
}
private synchronized boolean compareAndSwap(int expectedValue, int newValue) {
if (count == expectedValue) {
count = newValue;
return true;
}
return false;
}
}
在上述示例中,实现了一个使用CAS操作实现的计数器。increment方法先获取count变量的当前值,增加1后,通过do-while循环,使用compareAndSwap方法进行CAS操作,只有在操作成功时,才退出循环并返回。
- 使用CAS实现一个自旋锁。
public class CasSpinLock {
private volatile int state = 0;
private Thread lockingThread;
public void lock() {
while (!compareAndSet(0, 1)) {
// 自旋等待锁的释放
}
lockingThread = Thread.currentThread();
}
public void unlock() {
if (lockingThread != Thread.currentThread()) {
throw new IllegalMonitorStateException("unlocked by " + Thread.currentThread()
+ ", but locked by " + lockingThread);
}
state = 0;
lockingThread = null;
}
private synchronized boolean compareAndSet(int expectedState, int newState) {
if (state == expectedState) {
state = newState;
return true;
}
return false;
}
}
在上述示例中,实现了一个使用CAS操作实现的自旋锁。lock方法首先通过CAS操作将state变量的值设置为1,如果操作成功,则获取锁成功;否则,使用自旋等待锁的释放。unlock方法释放锁之前,需要检查当前线程是否持有锁。
通过上述两个示例,说明了CAS操作的实现原理和优劣性,并展示了如何使用CAS操作实现简单的计数器和自旋锁。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:关于Java 并发的 CAS - Python技术站