CAS和AQS---java
CAS
CAS(Compare-And-Swap,比较并交换),是一种原子操作,它的基本原理是:
- 比较:检查当前值是否等于预期值。
- 交换:如果相等,则修改为新值;否则,不做任何修改。
CAS 主要依赖 CPU 提供的原子指令(如 x86 的 cmpxchg 指令)
原子指令(Atomic Instructions)是 CPU 提供的一种特殊指令,保证操作不会被中断,可以一次性完成读取、计算和写入,从而确保并发环境下的线程安全。
原子变量(Atomic 类)
Java 提供 java.util.concurrent.atomic 包,实现了一系列基于 CAS 的原子变量,如:
- AtomicInteger:原子整型
- AtomicLong:原子长整型
- AtomicReference:原子引用类型
- AtomicStampedReference:解决 ABA 问题的版本控制原子引用
常见方法
- get() 获取当前值
- set(int newValue) 直接修改值(非原子)
- incrementAndGet() 自增 1
- decrementAndGet() 自减 1
- compareAndSet(int expect, int update) CAS 操作
示例
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println("Thread " + Thread.currentThread().getName() + " count: " + count.incrementAndGet());
}).start();
}
}
}
这里 incrementAndGet() 是原子操作,保证多个线程不会同时修改 count 而发生数据竞争。
CAS存在问题
- ABA 问题:如果一个变量从 A → B → A,CAS 可能误认为它没有变化。
- 解决方案:使用AtomicStampedReference 记录版本号,避免误判。
- 自旋开销大:如果 CAS 频繁失败,线程会不断重试,消耗 CPU 资源。
- 只能更新单个变量:CAS 不能直接更新多个变量,需用 AtomicReference 组合多个变量。
AQS
AQS 通过状态变量 state(一个 volatile int 变量)和CLH 队列(FIFO 等待队列)来管理线程的同步访问。
AQS 是 Java 并发包的核心框架,用于构建锁和同步器(如 ReentrantLock、Semaphore)。
案例引入
public class Main {
public static void main(String[] args) throws InterruptedException {
int[] count = new int[]{1000};
List<Thread> threads = new ArrayList<>();
// Lock lock = new ReentrantLock();
MyLock lock = new MyLock();
for (int i = 0; i < 5; i++) {
threads.add(new Thread(() -> {
for (int i1 = 0; i1 < 5; i1++) {
// lock.lock();
count[0]--;
}
for (int i1 = 0; i1 < 5; i1++) {
// lock.unlock();
}
}));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println(count[0]);
}
}
在主程序里,当把锁相关的代码注释后,就会造成并发安全的问题
那么我们能不能尝试造这样一把锁
锁的实现
public class MyLock {
// 原子变量,指示是否拿到锁
AtomicInteger state = new AtomicInteger(0);
// 指示拥有锁的线程
Thread owner = null;
AtomicReference<Node> head = new AtomicReference<>(new Node());
AtomicReference<Node> tail = new AtomicReference<>(head.get());
void lock() {
if (state.get() == 0) {
if (state.compareAndSet(0, 1)) {
// 拿到锁
System.out.println(Thread.currentThread().getName() + "直接拿到锁");
owner = Thread.currentThread();
return;
}
}
else {
if (owner == Thread.currentThread()) {
System.out.println(Thread.currentThread().getName() + " 拿到了重入锁,当前重入次数为" + state.incrementAndGet());
return;
}
}
// 没有拿到锁,把自己加入到阻塞链表
Node current = new Node();
current.thread = Thread.currentThread();
while (true) {
// 不断尝试获取当前链表尾结点,把自己加入尾结点后面
Node currentTail = tail.get();
if (tail.compareAndSet(currentTail, current)) {
// 自己成为尾结点,修改链表结构
System.out.println(Thread.currentThread().getName() + "加入到了链表尾");
current.pre = currentTail;
currentTail.next = current;
break;
}
}
while (true) {
// 尝试自己自己唤醒自己一次(防止自己还没加入链表,拥有锁的线程已经结束了唤醒函数,这时就没有人再去唤醒了)
if (current.pre == head.get() && state.compareAndSet(0, 1)) {
owner = Thread.currentThread();
head.set(current);
current.pre.next = null;
current.pre = null;
System.out.println(Thread.currentThread().getName() + "被唤醒之后,拿到锁");
return;
}
// 阻塞当前线程
LockSupport.park();
}
}
void unlock() {
if (Thread.currentThread() != this.owner) {
throw new IllegalStateException("当前线程并没有锁,不能解锁");
}
int i = state.get();
if (i > 1) {
state.set(i - 1);
System.out.println(Thread.currentThread().getName() + "解锁了重入锁,重入锁剩余次数" + (i - 1));
return;
}
if (i <= 0) {
throw new IllegalStateException("重入锁解锁错误!");
}
Node headNode = head.get();
Node next = headNode.next;
state.set(0);
if (next != null) {
System.out.println(Thread.currentThread().getName() + "唤醒了" + next.thread.getName());
// 唤醒下一个线程结点
LockSupport.unpark(next.thread);
}
}
class Node {
Node pre;
Node next;
Thread thread;
}
}
参考:
https://blog.csdn.net/qq_35923846/article/details/140186945?ops_request_misc=%257B%2522request%255Fid%2522%253A%25227793918c79a5db9a8efbab96fc0d36d2%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=7793918c79a5db9a8efbab96fc0d36d2&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-2-140186945-null-null.142v101pc_search_result_base1&utm_term=aqs&spm=1018.2226.3001.4187
https://github.com/implement-study/lock_demo/tree/main/src/main/java/tech/insight
https://blog.csdn.net/u014745069/article/details/117875073?ops_request_misc=%257B%2522request%255Fid%2522%253A%25227793918c79a5db9a8efbab96fc0d36d2%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=7793918c79a5db9a8efbab96fc0d36d2&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-4-117875073-null-null.142v101pc_search_result_base1&utm_term=aqs&spm=1018.2226.3001.4187