1.先谈谈什么是AQS:其实本质上AQS就是一个类,全称AbstractQueuedSynchronizer
在这个类中,有几个属性(主要有一个属性private volatile int state)和一个双向队列(也称CLH队列),属于并发包(JUC)下的一个基类
很多并发包下的类都是基于AQS实现的,比如:CountDownLatch,Semaphore,ReentrantLock等
先来看看AbstractQueuedSynchronizer这个类的结构吧
先来看一下Node这个静态内部类,对一些属性进行简单的介绍
static final class Node {
//这是排他锁的一个标志
static final Node EXCLUSIVE = null;
//如果带有这个属性,证明是失效了
static final int CANCELLED = 1;
//具有这个标识,证明后继节点要被唤醒
static final int SIGNAL = -1;
//Node对象存储标识的地方
volatile int waitStatus;
//指向前一个节点
volatile Node prev;
//指向后一个节点
volatile Node next;
//当前Node绑定的线程
volatile Thread thread;
//返回前驱节点,如果前驱节点为null,则抛出NullPointerException
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
}
这里简单介绍一下volatile关键字吧
主要有3大特点:
1)可以保证可见性(就是一个线程修改完值,另外线程可以知道)
2)不能保证原子性(就是有线程安全问题)
3)禁止指令重排(就是操作代码的指令是固定的,这里涉及到了计算机底层,了解就行)
2。来看看AQS的大概的结构吧
图大概是这样的:
其实AQS内部还做了一些工作,会创建出一个哨兵节点,放到第一个位置,然后才和其它节点相连
哨兵节点的作用:1)监控之后节点的状态,2)保证了线程安全
3.我们来开始分析源码吧
因为很多类都是基于AQS实现的,那我们就从ReentrantLock入手吧
1)首先加锁
当我们调用加锁方法时
其实调用了
这个sync其实是ReentrantLock的一个属性,sync分为公平和非公平
这个类继承了AQS
ReentrantLock默认是非公平锁,我们先从非公平锁入手
final void lock() {
//采用CAS发方式尝试将state的状态从0改为1,如果返回true,则修改成功(不知道CAS大家可以去了解一下)
if (compareAndSetState(0, 1))
//将一个属性设为当前线程,而这个属性是AQS的父类提供的
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
2)查看acquire方法
public final void acquire(int arg) {
//tryAcquire再次尝试获取锁资源,如果尝试成功,则返回true
if (!tryAcquire(arg) &&、
//尝试获取锁失败,将当前线程封装成Node对象,追加到AQS队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//线程中断
selfInterrupt();
}
3)查看tryAcquire方法
点进去发现,直接抛出了一个异常,说明这是一个模板方法
找到它的实现
进入NonfairSync中
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取AQS中的state
int c = getState();
//如果state为0,则再次尝试获取锁资源
if (c == 0) {
//这行代码似曾相识,采用CAS发方式尝试将state的状态从0改为1,如果返回true,则修改成功
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//占有锁资源的线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
//将state加1
int nextc = c + acquires;
//如果加1后,小于0,则超出可重入锁的最大范围,抛出Error
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//没问题,就对state进行赋值
setState(nextc);
return true;
}
return false;
}
查看addWaiter方法
//走到这里说明前面获取锁资源失败
private Node addWaiter(Node mode) {
//创建Node类,并设置thread为当前线程,设置为排他锁,mode就是传过来的Node.EXCLUSIVE
Node node = new Node(Thread.currentThread(), mode);
//获取AQS队列中的尾部节点
Node pred = tail;
//如果tail不为null,说明队列中有其它线程
if (pred != null) {
//当前节点的prev设置当前节点(就是新创建的node节点)为尾节点
node.prev = pred;
//基于CAS的方式,将tail节点设置为当前节点
if (compareAndSetTail(pred, node)) {
//将之前为next节点的next,设置为当前节点
pred.next = node;
return node;
}
}
//进入enq方法,在下面
enq(node);
return node;
}
---------------------------------------------------------------
//走到这里说明队列为空,或者CAS失败
private Node enq(final Node node) {
for (;;) {
//重新获取当前的tail节点为t
Node t = tail;
if (t == null) { // Must initialize
//现在没人排队,都是空
if (compareAndSetHead(new Node()))
//这里创建了一个新的节点作为头节点,这个head没有意义(相对于前面说的哨兵节点)
//将头尾都指向了这个node节点
tail = head;
} else {
//有人排队,往队列尾巴里塞
node.prev = t;
//基于CAS的方式,将tail节点设置为当前节点
if (compareAndSetTail(t, node)) {
//将之前为next节点的next,设置为当前节点
t.next = node;
return t;
}
}
}
}
查看 acquireQueued方法
//已经将node节点加入的当前的对列中,执行一下方法
final boolean acquireQueued(final Node node, int arg) {
//标识!
boolean failed = true;
try {
//标识!
boolean interrupted = false;
for (;;) {
//获取当前节点的前驱节点p
final Node p = node.predecessor(); //这里出现异常的概率几乎为0
//如果p是head,再次尝试获取锁资源(将state从0变为1,或者重入锁操作)
if (p == head && tryAcquire(arg)) {
//设置head节点为当前节点,并且将thread,pred都设置为null,因为拿到锁,就不需要排队了
//这时当前的节点就变成的哨兵节点,而之前的哨兵节点等待gc回收
setHead(node);
p.next = null; // help GC,加快回收
failed = false; //将标识修改为false
return interrupted;//返回interrupted
}
//保证上一个节点是-1,才会返回true,将线程阻塞,等待唤醒锁资源
if (shouldParkAfterFailedAcquire(p, node) &&
//基于Unsafe类的par方法,挂起线程
parkAndCheckInterrupt())//这里是可能出现异常的地方,JVM内部出现问题时,所以finally代码块中指向的概率 为0
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
------------------------------shouldParkAfterFailedAcquire方法------------------------------
//node是当前节点,pred是上一个节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取上一个节点的状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//如果上一个节点的状态为SIGNAL,说明一切正常
return true;
//如果上一个节点已经失效
if (ws > 0) {
do {
//将当前节点的pred指针指向上一个的上一个
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);//一直找到小于等于0的
//将重新标识的最近的有效节点的next
pred.next = node;
} else {
//小于等于0,不等于-1,将上一个有效节点的状态改为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
接下来释放锁:这期太长了,下期见!!!