AQS源码解析

本文深入解析了Java并发库中的AbstractQueuedSynchronizer(AQS),它是ReentrantLock、ReentrantReadWriteLock等同步组件的基础。AQS通过volatile变量state和双指针管理同步等待队列,并利用CAS(Compare And Swap)操作保证线程安全。CAS是一个CPU原语,用于在无锁情况下更新state值,避免了锁的开销。同时,文章还介绍了如何通过Unsafe类的getAndAddInt方法解决ABA问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

AbstractQueuedSynchronizer,简称AQS。它为java.util.conturrent包下的常用同步组件,比如ReentrantLock,ReentrantReadWriteLock 等提供了基础支撑。AQS的子类如下图所示。
继承AQS的类

AQS的内部结构

public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {

    // 同步等待队列的头指针
    private transient volatile Node head;
    // 同步等待队列的尾指针
    private transient volatile Node tail;
    /**
     * 同步器的状态。根据不同是实现有不同的含义,是AQS的核心。
     * 比如 ReentrantLock 用这个state的值代表是否被加锁和重入的次数
     */
    private volatile int state;
    
    // 同步等待队列节点的定义
    static final class Node {
        // 头指针
        volatile Node prev;
        // 尾指针
        volatile Node next;
        // 节点中存放一个线程的引用
        volatile Thread thread;

        // ... 省略部分代码...
    }

	// CAS操作进行修改 state 的值
	protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

    // ... 省略部分代码...
}

从代码中可以看出,AQS的内部有一个 volatile 声明的 state 和 两个指针,指向了AQS等待队列的头部和尾部。其结构大致如图:
AQS内部结构

AQS原理解析

  • CAS + volatile
    从上面的图中,我们可以看出来,AQS就是多个线程去竞争一个 state 占有权的一个类,state用 volatile修饰,是为了保证线程间可见,然后线程在竞争 state 的时候,用CAS操作保证了对 state 修改的原子性。
  • CAS原理
// Unsafe.class 类中 compareAndSwapInt 的定义
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

从定义中我们可以看到,CAS方法是一个 native 方法,也就是说CAS操作是CPU原语支持的,保证了线程安全。

cas(Value, Offset, Expected, NewValue)
	if  Value== Expected
		Value = NewValue
	otherwise try again or fail

大概的意思就是当前值(Value)是我们期望的那个值(Expected),没有被其他线程修改过,那么就将它改为新的值(NewValue),否则重试或者失败。为了防止ABA问题(即一个线程将值从A改为B,然后另一个线程又将值从B改为A,这会让我们当前线程以为没有被其他线程操作过,这种ABA问题对于基本数据类型而言,结果没有什么影响,但是对于复杂的引用对象而言,我们是不知道这个对象内部是否被其他线程操作过什么的。为了解决这个问题,解决方案是为这个值带一个 Offset,值每被修改一次,这个Offset就修改一次,我们用 Value + Offset 的方式避免了 ABA 问题的出现)

我们可以从 Unsafe 类中的 getAndAddInt 来体会一下 ABA 问题的解决方案:

	// Unsafe.class
	
    /**
     * 当对象o的值 和对象o在 给定offset偏移量(版本)下的值相同时,将当前对象设置为 o + delta
     * @param o 当前对象
     * @param offset 偏移量(版本)
     * @param delta 要add的值
     * @return  int 添加后的结果
     */
    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            // 获取对象o在给定offset偏移量(版本)下的值
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值