一篇文章带你读懂AQS

一:概述

1 同样的AQS也是一个缩写,指的是Java中的一个类AbstractQueuedSynchronizer,这是一个抽象父类,可以用于实现各种同步工具,例如ReentrantLock、Semaphore、CountDownLatch
2 AQS统一规范了锁的实现,屏蔽了同步状态管理、同步队列的管理和维护、阻塞线程排队和通知、唤醒机制等
是一切锁和同步组件实现的----公共基础部分
3 AQS使用一个volatile的int类型变量state来表示同步状态,默认是0,代表资源没有被占用,是空闲状态
通过内置的FIFO队列来完成资源获取的排队工作,将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对锁的修改

二:ReentrantLock

通过以上,知道了AQS基本概念,那么现在看一下一个用AQS实现的锁ReentrantLock,当它调用lock方法时

 public void lock() {
        sync.lock();
    }

其实是使用了Sync 的lock方法,下面可以很清晰的看到:Sync 继承了AQS

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    abstract void lock();

1 构造方法

当构造方法中不传参数,默认到以下,默认是非公平锁

   public ReentrantLock() {
        sync = new NonfairSync();
    }

当构造方法中传参数true,默认到以下,默认是公平锁

 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        //即传true 就 new FairSync() 是公平锁,否则 new NonfairSync() 非公平锁
    }

2 非公平锁的lock方法

 static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

可以看到,这里还是一个CAS操作compareAndSetState(0, 1),将状态0改为1,即代表抢到锁
并且通过 setExclusiveOwnerThread(Thread.currentThread())方法,将持有锁的线程设置为当前线程
如果获取锁失败,则进入 acquire(1)方法,接下来会介绍

3 公平锁的lock方法

和非公平锁竞争失败一样,这里之间调用了acquire(1)方法

 final void lock() {
            acquire(1);
        }

那现在我们看看这个acquire(1)到底做了什么事情

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

可以看到这里首先调用了tryAcquire(arg)方法,这个方法对于公平锁非公平锁的实现是不同的,但是两个方法间主要差别在

hasQueuedPredecessors() 方法,这个方法下面的公平锁使用到,但是非公平锁就没有

    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;
    }
}

4 hasQueuedPredecessors()方法

第三节中已经谈到非公平锁和公平锁的差别主要是 hasQueuedPredecessors()方法,那么这个方法具体做了什么呢?

public final boolean hasQueuedPredecessors() 
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

这里主要是判断前面有没有排队的线程 如果返回true说明队列中有等待的线程在当前线程之前, 返回false,说明当前线程是头节点,队列为空

5 addWaiter

在调用tryAcquire方法时,如果返回false,即没有获得锁,则进入addWaiter方法

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);
    return node;
}

第一个线程进入方法时,此时tail为null,所以会进入enq(node)即入队

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

1 因为此时tail==null,就进入compareAndSetHead(new Node())方法,主要这里新创建了一个Node对象,即虚拟节点
2 compareAndSetHead的作用就是将传入的虚拟节点设置为头节点,又赋值给tail
3 由于for(::)是个循环,此时第二次进来,此时的tail已经在上一步被赋值,则进入else代码块
4 将t作为当前节点的前置节点 , 将当前节点设置为尾节点 , 将当前节点作为t的后继节点

6 acquireQueued

第五节中enq方法入队后返回一个node,此时就进入了 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);
    }
}

可以看到中途又尝试获取锁,后调用了parkAndCheckInterrupt方法

 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

可以看到,此时使用了中断机制LockSupport.park(this)中断线程

你可能感兴趣的:(java,开发语言)