java并发之AbstractQueuedSynchronizer

267 阅读5分钟

Lock接口

jdk1.5之后新增了lock接口,它提供了与synchronized关键字类似的同步功能,它提供了一些synchronized没有的一些特性,如可以响应中断,超时获取锁,获取锁的过程更加轻量级等。

ReentrantLock的底层依赖于AbstractQueuedSynchronizer实现。

队列同步器

AbstractQueuedSynchronizer是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成线程的排队工作.

同步器的设计是基于模板方法模式的,也就是说,同步器实现了了同步状态管理、线程的排队、等待与唤醒等底层操作,而使用者只需要关注同步状态的获取与释放。

image.png

重写同步器指定的方法时,需要使用同步器提供的如下3个方法来访问或修改同步状态。

  • getState():获取当前同步状态。
  • setState(int newState):设置当前同步状态。
  • compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态设置的原子性。 image.png

总结:同步器是实现任意同步组件的关键,同步组件它定义了使用者与同步组件交互的接口(比如可以独占的获取锁,或者共享的获取锁),隐藏了实现细节;同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。

ReentrantLock的底层实现和AQS的原理

非公平获取锁

image.png

可以看出NonfairSync继承Sync,而Sync又继承了AbstractQueuedSynchronizer,底层还是通过AQS去实现的。

lock方法很简单,上来先cas修改state状态为1(尝试获取锁)。获取锁成功,则将当前线程设置为独占线程。失败就进入acquire方法。

acquire

image.png

tryAcquire

tryAcquire方法就是尝试去获取锁的过程。

image.png 其实就是判断同步状态是否为0,为0则尝试获取锁。这里也可以看出lock是可重入锁,不为0的情况下会去判断独占线程是否是当前线程,如果是则将同步状态加1。(当然如果重入的次数超过int的最大值,溢出了则抛出异常。)

addWaiter

image.png

image.png 获取不到锁的线程就会组装成一个节点加入到队列中。

队列的属性如下: image.png

  • addWaiter方法获取尾节点,如果尾节点不为空,则设置当前节点是为尾节点。
  • 如果尾节点为空,或者cas设置当前节点为尾节点失败,就进入enq方法,死循环不停的cas尝试设置当前节点为尾结点。注意的是,当尾节点为空时,则说明队列也为空,会初始化一个节点为首节点,并且把首节点也设置成尾节点,而且初始化的节点是一个new Node()并不是设置当前线程的node,是一个虚节点。

acquireQueued

addWaiter是将线程加入到队列中,而acquireQueued是队列中的线程不停的循环尝试获取锁。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 获取前驱节点
            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) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        // 前驱节点的状态是等待被唤醒(也可能已经获取锁了)
        return true;
    if (ws > 0) {
        // 大于0就是取消的节点,队列中跳过取消节点,
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 设置前驱节点状态为SIGNAL
        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;
        // 头节点不为空,且头节点不是初始化节点
        if (h != null && h.waitStatus != 0)
            // 唤醒后继节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}
protected final boolean tryRelease(int releases) {
    // 获取同步状态-1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 同步状态为0,持有线程释放锁
    if (c == 0) {
        free = true;
        // 设置独占线程为null
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
private void unparkSuccessor(Node node) {
    // 获取头节点状态
    int ws = node.waitStatus;
    if (ws < 0)
        // 设置头节点,状态为0
        compareAndSetWaitStatus(node, ws, 0);

    // 获取下一个节点
    Node s = node.next;
    // 下一个节点为null,或者是取消节点
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从队列尾部,一直获取到有效节点
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 唤醒该节点的线程
    if (s != null)
        LockSupport.unpark(s.thread);
}

非公平锁和公平锁的区别

非公平锁

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        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");
        setState(nextc);
        return true;
    }
    return false;
}

公平锁

final void lock() {
    acquire(1);
}
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;
}
  • 非公平锁上来直接尝试获取锁,看是否能抢占锁,
  • 公平锁会调用hasQueuedPredecessors方法,判断当前队列是否有有效节点,没有有效节点才会去获取锁。