1.CAS原理
1.1 CAS的工作原理
CAS,即Compare-And-Swap或比较并交换,是实现并发算法时常用的一种机制。它涉及三个操作数——内存位置V(Variable),预期原值A(Anticipated value),以及新值B(New value)。其基本操作是:当且仅当V的值等于A时,CAS才会通过原子方式用新值B来更新V的值,否则不进行任何操作。让我们通过代码示例进一步理解CAS机制:
import java.util.concurrent.atomic.AtomicInteger;
class CASExample {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
int expect;
do {
expect = count.get(); // 获取当前值
// 此处可能会有多个线程尝试更新同一个count
} while (!count.compareAndSet(expect, expect + 1)); // CAS操作
}
public int getCount() {
return count.get();
}
}
1.2 锁自旋与性能优化
传统的阻塞锁在等待锁的过程中会使线程进入睡眠,这会导致线程上下文切换,带来额外的开销。因此,引入了一种轻量级的锁机制——锁自旋。它通过不断循环等待(自旋),检查锁的状态,以尝试获取锁。这种方式在多核处理器上,如果等待时间不长的话,能够减少线程状态变更的开销,提高系统整体性能。例如,在JDK中,可以通过-XX:+UseSpinning启用自旋锁。
1.3 Java在CAS上的实现实践
Java从1.5开始引入了java.util.concurrent.atomic包,它提供了一组利用CAS操作进行锁自旋的工具类,例如AtomicInteger,AtomicLong,AtomicReference等。这些类可以帮助开发人员以线程安全的方式操作单个变量,而无需使用synchronized关键字。
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceDemo {
public static void main(String[] args) {
AtomicReference<String> atomicString = new AtomicReference<>("Initial Value");
String previousValue = atomicString.getAndSet("New Value");
System.out.println("Previous Value: " + previousValue);
System.out.println("Current Value: " + atomicString.get());
}
}
2.CAS在并发编程中的应用
2.1 在并发容器中的使用案例
让我们看一个例子,其中CAS用于实现非阻塞的并发容器。如ConcurrentLinkedQueue,一个基于链接节点的无界线程安全队列,内部正是使用CAS来达到线程安全的。
import java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentLinkedQueueExample {
public static void main(String[] args) {
ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
queue.offer(1); // 将特定元素插入到此队列的尾部。
queue.offer(2);
System.out.println(queue.poll()); // 返回队列头部的元素,并从队列中移除
System.out.println(queue.poll());
}
}
2.2 从基本类型到数组和引用类型的原子操作
除了对基本数据类型的支持,atomic包还提供了数组和对象属性的原子更新器。AtomicIntegerFieldUpdater和AtomicReferenceArray是这类原子更新器的两个例子。
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
class AtomicUpdaterExample {
private static