AQS源码初探

AQS源码初探

AbstractQueuedSynchronizer(AQS)

AQS类为依赖先进先出队列并实现阻塞锁和相关同步器提供框架。ReentratLock和CountDownLactch等都是依赖它进行实现的。

Node类

Node类是先入先出队列的节点类。主要有5个成员变量,waitStatus(节点状态),prev,next(双向队列的两个指针),thread(所属线程), nextWaiter(标记当前节点独占/共享)实现如下:

static final class Node {
        //nextWaiter 可以取的值
        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;
        //waitStatus 可以取的值
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;

        volatile int waitStatus;

        //分别指向先驱节点和后继节点
        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        //取先驱节点为空则抛出空指针异常!
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

AQS重要的几个方法(独占模式)

1.acquire(int arg)

独占模式下获取锁,并忽略终端。获取锁的细节通过调用 boolean tryAcquire 方法实现,tryAcquire 拿不到锁会排队等待。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

2.tryAcquire(int arg)

AQS类没有对这个方法进行具体实现,这个方法要交给同步器自行实现。

protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

3.addWaiter(Node mode)

为当前线程创建一个Node并加入等待队列

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // 队列不为空且成功入队,将队尾改为插入的节点,并返回d
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 队列为空或者入队操作失败调用enq方法入队。
    enq(node);
    return node;
}

4.enq(final Node node)

private Node enq(final Node node) {
    //自旋直到将节点成功入队
    for (;;) {
        Node t = tail;
        //队列为空则初始化一个队列
        if (t == null) { 
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            //将节点插入队尾
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

5.acquireQueued(final Node node, int arg)

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        //初始化中断状态
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            //如果前驱节点就是队列的head节点(当前节点是队列的第一个节点),就尝试获取锁
            if (p == head && tryAcquire(arg)) {
                //该节点变为头节点(头节点没有对应线程,只有waitStatus和next参见下面的setHead方法),节点出队列,且节点对应线程拿到了锁
                setHead(node);
                p.next = null; // 将p节点的next指针置空,GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //如果等待过程中断过将中断状态置true
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

//setHead操作 将head指向node thread置空,prev置空
private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

6.shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //拿到前驱的状态
    int ws = pred.waitStatus;
    //前驱处于SIGNAL状态,则返回true
    if (ws == Node.SIGNAL)
        //可以中断
        return true;
    //ws>0即为ws为CANCEL状态,前驱节点不排队了,找一个排在自己前面且在排队的节点
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //找到后将这个节点状态设置为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //不中断,前驱节点取消了可以尝试获取锁。
    return false;
}

7.parkAndCheckInterrupt()

private final boolean parkAndCheckInterrupt() {
    //调用park方法阻塞线程
    LockSupport.park(this);
    return Thread.interrupted();
}

8. release(int arg)

public final boolean release(int arg) {
    // 调用tryRelease方法释放锁
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            //unpark head节点的后继节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}

9.tryRelease(int arg)

和tryAcquire 一样需要具体同步器自行实现

protected boolean tryRelease(int arg) {     
    throw new UnsupportedOperationException();
}

10.unparkSuccessor

//unpark node节点后继节点方法
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    //置零当前线程所在的结点状态为0(消除SIGNAL状态)。
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    //找到下一个需要唤醒的结点s
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        //找到后继中状态小于0的节点并修改为node的后继节点
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread;
}

ReentratLock (AQS 实际应用)

NonFairSync

ReentrantLock(可重入锁) 中的FairSync和NonfairSync继承了AQS类并实现了tryAcquire 和 tryRelease方法,ReentrantLock默认使用NonfairSync,让我们看一下NonFairSync tryAcquire的实现

final boolean nonfairTryAcquire(int acquires) {
    //获取当前线程和当前锁的状态
    final Thread current = Thread.currentThread();
    int c = getState();
    //c==0代表锁空闲 1代表锁被占用
    if (c == 0) {
        获取到了锁返回true
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //锁被自己占用,状态+1并且返回true
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

//直接调用上述方法
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

//Reentrant加锁方法,锁空闲就直接获取,否则调用AQS的acquire方法排队等待
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    //要当前线程是占有锁的线程才有资格释放锁
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // c==0 时才释放锁,c!=0说明重入锁被一个线程反复占用,要等这个线程全部释放。
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
FairSync

FairSync tryAcquire实现和非公平锁的区别就是取锁的时候多了个判断条件,判断队列是否为空和判断抢占锁的线程是否是队列第一个节点对应的线程

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 获取锁前要先判断是否有等待队列,如果有判断等待队列的头节点是否为当前节点
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
//这个方法是AQS里的
public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    //h != t 判断队列是否为空,并且判断当前线程能否竞争锁(是否是头节点的第一个后继)
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值