一、AQS
1、本质
AQS本质上就是Java中的AbstractQueuedSynchronizer(抽象队列同步器)类,在AbstractQueuedSynchronizer类中,有几个属性和一个双向队列;
AQS就是并发包下的一个基类,它的实现包括
CountDownLatch,ReentrantLock,Semaphore....
2、双向队列部分源码(AbstractQueuedSynchronizer类中的内部类)
static final class Node {
static final Node SHARED = new 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就抛出异常
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
}
二、加锁
ReentrantLock可以实现公平锁,也可以实现非公平锁,也是互斥锁和可重入锁(可重入锁原理)
1、从lock进入(ReentrantLock类)
public void lock() {
// sync分为公平和非公平
sync.lock();
}
2、从非公平锁进去(Sync类)
final void lock() {
// 通过CAS(轻量级锁的实现方式)的方式将state从0修改为1,如果返回true,代表修改成功
if (compareAndSetState(0, 1))
// 将一个属性设置为当前线程,这个属性是AQS的父类提供的
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
3、查看acquire方法(AQS类)
public final void acquire(int arg) {
// tryAcquire()再次尝试获取锁资源,如果尝试成功就直接放回true
// acquireQueued() 获取锁资源失败后,需要将当前线程封装成一个Node,追加到AQS的队列中
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 线程终端
selfInterrupt();
}
4、查看tryAcquire方法(AQS类)
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取AQS的state的值
int c = getState();
// 如果state为0代表尝试再次获取锁资源
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 判断当前占有锁资源的线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
// 将state值+1
int nextc = c + acquires;
if (nextc < 0) // overflow
// 超出锁可重入的最大值,抛出Error,增强代码健壮性
throw new Error("Maximum lock count exceeded");
// 没有问题就重新对state进行赋值
setState(nextc);
// 锁重入成功
return true;
}
// 锁重入失败
return false;
}
5、查看addWaiter方法(AQS类)
// 执行该方法说明前面获取锁资源失败,放到队列中等待
private Node addWaiter(Node mode) {
// 创建Node,并且设置thread为当前线程,设置为排他锁
Node node = new Node(Thread.currentThread(), mode);
// 获取AQS队列的尾部节点
Node pred = tail;
// 如果pred是null,表示队列是空队列,否则表示队列有人排队
if (pred != null) {
// 将当前节点的prev,设置为刚才的尾部节点,即把当前节点设置为新的尾节点
node.prev = pred;
// 基于CAS方式,将tail节点设置为当前节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
三、释放锁