一篇文章带你读懂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());elseacquire(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 orderNode 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 failureNode 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 initializeif (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 GCfailed = 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)
中断线程