AQS源码分析

文章目录

AQS

AQS为锁获取,锁释放的排队出队过程提供了一系列模版方法,juc包中许多类都是基于aqs创建的

队列中维护的是个fifo的双向链表,每个数据结构都有两个指针,可以从任意一个节点开始很方便的访问前驱节点和后继节点

每个节点由线程封装,当线程抢夺锁失败后会封装成节点加入aqs队列中,通过cas,自旋以及LockSupport.park()的方式维护state变量状态,获取锁的线程释放锁后,随机从队列中唤醒一个阻塞的节点

重量级基础框架及整个JUC体系的基石,主要用于解决锁分配给谁的问题

juc显示锁种类丰富,所以aqs将不同锁的具体操作抽取成钩子方法,各种锁的子类或者内部类可以去实现

钩子方法:

声明在抽象类中,一般用protected修饰,它可以是空方法(由子类实现),也可以是默认实现的方法

aqs定义了两种资源共享方式:

  • 独占
  • 共享

自定义同步器的共享方式要么是独占(ReentrantLock),要么是共享(CountDownLatch/Semaphore),但是aqs也支持同时实现独占和共享的两种方式(ReentrantReadWriteLock)

private transient volatile Node head;


private transient volatile Node tail;

//表示锁的状态,用volatile修饰保证了操作可见性
//state为0表示锁当前未被使用
private volatile int state;
protected final int getState() {
    return state;
}

protected final void setState(int newState) {
    state = newState;
}

//由于setSate无法保证原子性,aqs又提供了这个方法,利用底层Unsafe的cas机制实现原子性,实际上调用的是unsafe成员的compareAndSwapInt()方法
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

Node内部类

static final class Node {
    /** 表示线程以共享的模式等待锁 */
    static final Node SHARED = new Node();
    /** 表示线程以独占的方式等待锁 */
    static final Node EXCLUSIVE = null;

    /** 表示线程获取锁的请求取消了 */
    static final int CANCELLED =  1;
    /** 表示线程准备好了,就等资源释放了 */
    static final int SIGNAL = -1;
    /** 表示线程在等待队列中,节点线程等待唤醒 */
    static final int CONDITION = -2;
    /**
     * 当前线程处于SHARED的情况下,该字段才会使用
     */
    static final int PROPAGATE = -3;
    
    //初始化默认为0
    volatile int waitStatu s;

    volatile Node prev;

    volatile Node next;

    volatile Thread thread;

    Node nextWaiter;

    /**
     * Returns true if node is waiting in shared mode.
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

下面以ReentrantLock为例对AQS的源码进行分析

源码分析

//这里调用的是静态内部类Sync的lock方法
public void lock() {
    sync.lock();
}

Sync(继承于AQS) 有两个实现类NonfairSync和FairSync

Lock接口的实现类基本都是通过聚合了一个队列同步器的子类完成线程访问控制的

这里以非公平锁为例

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     * (公平锁是直接调用acquire方法)
     */
    final void lock() {
        //不排队直接尝试cas抢抢不到再调用acquire方法
        if (compareAndSetState(0, 1))//将state加一成功
            //设置当前线程拥有独占访问权限
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //尝试获取锁,如果获取不到,进入等待队列,阻塞线程
            acquire(1);
    }

    //尝试获取锁
    /* 锁已经被其他线程获取
     * 锁没有被其他线程获取,但是当前线程需要排队(公平锁)
     * cas抢锁失败
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
public final void acquire(int arg) {
    //tryAcquire抢锁成功皆大欢喜
    if (!tryAcquire(arg) &&
        //抢锁不成功addWaiter进入等待队列
        //acquireQueued使线程进入队列后,进入阻塞状态
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        /* 公平锁这里会多判断一个!hasQueuedPredecessors()是和非公平的tryAcquire方法唯一的区别
         * 查看是否有任何线程等待的时间超过当前线程
         */
        //锁目前没有被占用,cas抢占锁
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //如果当前线程就是持有锁的线程
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        //增加state的值,(即ReentrantLock可重入锁的原理)
        setState(nextc);
        return true;
    }
    //若锁既被占用,占用的线程又不是当前线程,返回false
    return false;
}
private Node addWaiter(Node mode) {
    //按照传来的模式创建一个新的节点
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    //队列不为空时,将节点入队
    if (pred != null) {
        //当前节点前置指针指向队列尾节点
        node.prev = pred;
        //将当前节点设置成尾结点
        if (compareAndSetTail(pred, node)) {
            //原来尾结点的后置指针指向当前节点
            pred.next = node;
            return node;
        }
    } 
    //当队列为空时,需先将队列初始化再将节点入队,enq会初始化一个空的Node作为队列的head
    enq(node);
    return node;
}

acquireQueued方法是aqs的核心,在将节点加入等待队列后执行

  1. 判断这个节点的前一个节点是否是head,如果是说明这个节点是下一个可以获取锁的线程,就尝试获取锁,如果成功,将他设置为head,原来的head移除队列
  2. 如果当前节点的前一个节点不是头节点或者抢锁不成功,就判断其前一个节点的waitState是否为SIGNAL,是,那么当前线程进入阻塞状态,如果是0则设置成SIGNAL,如果大于0,表示前节点已经被取消了,需要从队列中移除
  3. for循环……

两种情况会导致线程阻塞结束

  • 线程排在队头,持有锁的线程释放锁后,将这个线程unpark了
  • 线程被Interrupt了,在外部Interrupt线程,不是抛出InterruptException,这里和sleep,wait阻塞不一样
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {//这段代码循环执行直到当前节点的前一个节点是头结点,且当前节点抢锁成功
            //获取node的前置节点
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //如果当前节点不是头结点或者抢锁不成功
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            //可能有队列中的节点不想等了需要出队(可能是一个也可能是多个)
            cancelAcquire(node);
    }
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //获取前一个节点的waitStatus
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)//如果前一个节点status为-1
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        
        //将前一个节点的waitStatus设置成-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
//阻塞当前线程并检查当前线程是否被打断
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

释放锁

public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    //如果锁已经被全部释放
    if (tryRelease(arg)) {
        Node h = head;
        //如果头结点不为null且头结点的status不为0
        if (h != null && h.waitStatus != 0)
            //如果头结点的下一个节点不是null,就将这个节点线程唤醒抢锁
            unparkSuccessor(h);
        return true;
    }
    return false;
}
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

ReetrantLock的lock和unlock方法调用的都是Sync的方法,而Sync继承了AQS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值