AQS中最基本的数据结构是Node,Node即为CLH变体队列中的节点。
static final class Node { // 表示线程以共享的模式等待锁 static final Node SHARED = new Node(); // 表示线程正在以独占的方式等待锁 static final Node EXCLUSIVE = null; // 为1,表示线程获取锁的请求已经取消了 static final int CANCELLED = 1; // 为-1,表示线程已经准备好了,就等资源释放了 static final int SIGNAL = -1; // 为-2,表示节点在等待队列中,节点线程等待唤醒 static final int CONDITION = -2; // 为-3,当前线程处在SHARED情况下,该字段才会使用 static final int PROPAGATE = -3; // 当前节点在队列中的状态 volatile int waitStatus; // 前驱节点 volatile Node prev; // 后续节点 volatile Node next; // 当前节点的线程 volatile Thread thread; // 指向下一个处于CONDITION状态的节点 Node nextWaiter; ... }AQS中CLH变体的虚拟双向队列(FIFO),AQS是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配。
// 队列头节点 private transient volatile Node head; // 队列尾节点 private transient volatile Node tail;在AQS中的队列是一个FIFO队列,它的head节点永远是一个虚拟结点(dummy node), 它不代表任何线程,因此head所指向的Node的thread属性永远是null。但是我们不会在构建过程中创建它们,因为如果没有争用,这将是浪费时间。 而是构造节点,并在第一次争用时设置头和尾指针。只有从次头节点往后的所有节点才代表了所有等待锁的线程。也就是说,在当前线程没有抢到锁被包装成Node扔到队列中时,即使队列是空的,它也会排在第二个,我们会在它的前面新建一个虚拟节点。
4. 获取锁实现 4.1 ReentrantLock 独占锁内部结构构造函数源代码
// 默认创建非公平锁 public ReentrantLock() { sync = new NonfairSync(); } // 通过传值为true来进行创建公平锁 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }ReentrantLock 里面有三个内部类:
一个是抽象的 Sync 实现了 AbstractQueuedSynchronizer
NonfairSync 继承了 Sync
FairSync 继承了 Sync
4.2 非公平锁的实现ReentrantLock 种获取锁的方法
public void lock() { sync.lock(); }ReentrantLock 的非公平锁实现
static final class NonfairSync extends Sync { final void lock() { // 如果设置state的值从0变为1成功 if (compareAndSetState(0, 1)) // 则将当前线程设置为独占线程 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }compareAndSetState(0,1)
protected final boolean compareAndSetState(int expect, int update) { // 通过unsafe.compareAndSwapInt方法来进行设置值 return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }stateOffset 为AQS种维护的state属性的偏移量
setExclusiveOwnerThread(Thread.currentThread());
protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; }acquire(1); 调用的是AQS 中的acquire(int arg) 方法
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }