1. AQS抽象队列同步器
AQS的全称是AbstractQueuedSynchronizer,翻译过来就是抽象队列同步器。这个类是在java.util.concurrent.locks包下面。

AQS就是一个抽象类,是用来构建锁和同步器的一个底层。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
}
AQS底层构建锁和同步器提供了一个通用的功能的实现,因此使用AQS可以构建出应用广泛的同步器,比如我们常见的ReentrantLock,CountDownLatch,SynchronousQueue等等都是基于AQS的。
1.1 AQS的原理是什么?
AQS的核心思想是:如果被请求的共享资源空闲(state = 0),则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态(state: 0 -> 1)。如果被请求的共享资源被占用,那么就需要以掏线程阻塞等待以及唤醒时锁的分配机制,这个机制AQS是基于CLH队列锁实现的,即将暂时获取不到锁线程加入到队列中。
互斥:使用CAS将State变量从0改成1,并且只有一个线程会成功!
有一个地方会存放阻塞了的所有的线程
双向队列,CLH队列(Cfafasfas,Lfafsaf,Hfasfasf)
AQS是将每条请求共享资源的线程封装成一个节点(Node)来实现锁的分配。在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用、当前线程在队列中的状态、前驱节点、后继节点。
static final class Node {
volatile int waitStatus; //节点状态 ×
volatile Node prev; //前驱节点
volatile Node next; //后继节点
volatile Thread thread; //当前尝试加锁的线程
}

AQS(AbstractQueuedSynchronizer)的核心原理图。

AQS使用一个int成员变量state表示同步状态,通过内置的线程等待队列来完成资源线程的排队工作。
/**
* The synchronization state.
*/
private volatile int state;
另外,状态信息state可以通过protected类型的getState()、setState()和compareAndSetState()进行操作。并且,这几个方法都是使用final修饰的,在子类中无法被重写。
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
//使用CAS保证线程安全
protected final boolean compareAndSetState(int expect, int update) {
return STATE.compareAndSet(this, expect, update);
}
以ReentrantLock为例,state初始值为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占锁并将state + 1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state = 0为止,其他线程才会有机会来获取锁。当然,释放锁之前,A线程自己是可以重复获取锁的(state++),这就是可重入锁,但是要注意的是。获取多少次释放多少次,这样才会保证state回到0。
以CountDownLatch为例,任务分成N个子线程去执行,state直接初始化为N(N和线程个数一致)。每个线程countDown()一次,state会CAS(Compare And Swap)减1。等到所有子线程都执行完后(state = 0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后续动作。
2. ReentrantLock源码分析
ReentrantLock底层源码剖析

2.1 非公平锁加锁流程
static final class NonfairSync extends Sync {
final void lock() {
//ReentrantLock底层默认非公平锁
//1.使用cas对state变量进行修改,state=0 => state=1
if (compareAndSetState(0, 1))
//2.如果修改成功,那么设置获取锁的线程为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
//3.加锁失败,进入acquire(1)方法
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
如果不出现竞争的情况,直接cas就加锁成功。
当第一个竞争出现的时候就走acquire(1)方法:

Thread-1执行了:
- CAS尝试将state由0改成1,结果失败
- 进入tryAcquire逻辑,这时候state已经是1,结果仍然失败。
public final void acquire(int arg) {
//tryAcquire再次尝试加锁,如果加锁不成功,就添加到阻塞队列
if (!tryAcquire(arg) &&
//addWaiter 创建一个node结点对象,并加入到等待队列
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
-
接下来进入addWaiter逻辑,构造Node队列
- 图中waitStatus表示该结点状态,其中0为默认正常状态。
- 其中第一个结点Node是Dummy哨兵结点,用来占位,不关联线程

-
当线程进入acquireQueued逻辑
- acquireQueued会在一个死循环中不断尝试获取锁,失败后进入park阻塞。
- 如果自己是紧邻着head(第二位),那么再次tryAcquire尝试获取锁,这时候state=1,获取锁失败。
- 进入shouldParkAfterFailedAcquire逻辑,将前驱node,即head的waitStatus修改为-1,返回false。
![[图片]](/https://i-blog.csdnimg.cn/direct/1ab2985e22f94a12b51e9b2e7860a99e.png)
-
shouldParkAfterFailedAcquire执行完毕后,回到acquireQueued,再次tryAcquire尝试获得锁,当然这时候state=1,加锁失败。
-
当再次进入shouldParkAfterFailedAcquire时,这是因为前驱node的waitStatus已经是-1,这时候返回true
-
进入parkAndCheckInterrupt()调用LockSupport.park(this)阻塞加锁失败的线程。
//添加一个结点
private Node addWaiter(Node mode) {
//1. 创建一个新节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//2. 如果尾结点不为空,那么尝试直接入队(在下面的方法会再次入队)
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//3. 结点入队
enq(node);
return node;
}
//入队的方法
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 4.初始化哨兵结点
if (compareAndSetHead(new Node()))
tail = head;
} else {
//5. 将结点挂在虚拟节点后边(和步骤2一样)
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
//阻塞的方法acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//1. 获取当前结点的前驱节点
final Node p = node.predecessor();
//2. 如果发现当前节点是哨兵结点后的第一个结点,尝试加锁
//(可能会竞争失败:因为是非公平锁)
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//3. shouldParkAfterFailedAcquire将node的前驱变成-1
if (shouldParkAfterFailedAcquire(p, node) &&
// 4.阻塞在这个地方!!!!
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//阻塞当前线程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
2.2 解锁流程
解锁的时候调用unlock()即可。

public void unlock() {
//解锁
sync.release(1);
}
public final boolean release(int arg) {
//if尝试解锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后继线程
unparkSuccessor(h);
return true;
}
return false;
}
//解锁流程 => 修改state和exclusiveThread持有锁的线程
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//唤醒线程
private void unparkSuccessor(Node node) {
//将当前结点状态设置为0
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//唤醒下一个阻塞的线程
if (s != null)
//线程在acquireQueued函数醒过来
LockSupport.unpark(s.thread);
}
2.3 ReentrantLock可重入原理
ReentrantLock在tryAcquire()获得锁的时候,判断当前线程和持有锁的线程是不是一个,如果是,那么就将state状态加1。
//尝试获取锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//加锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//判断锁是不是重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//解锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
2.4 ReentrantLock可打断原理
在线程调用LockSupport.park()阻塞的过程中,如果被打断,那么会直接throw一个异常,而不会继续尝试加锁。
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//尝试进行加锁
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
//可打断逻辑
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//在park过程中如果被interrupt,会进入这里
//这时候直接抛出异常,而不会再次进入for(;;)循环
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2.5 公平锁和非公平锁设计
公平锁和非公平锁的区别主要在于tryAcquire方法:
- 非公平锁每次tryAcquire(1)的时候,会直接判断state是不是为1,如果为1,那么直接调用cas尝试加锁。
- 公平锁每次加锁会先检查AQS队列中是否有前驱节点,没有才会去竞争锁。
公平锁加锁代码:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//会检查AQS队列中是否有前驱节点,没有才会去竞争
//or如果我是第一个结点,也会竞争
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;
}
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
//h != t 表示队列中有Node
return h != t &&
(
//(s = h.next) == null表示队列中还没有老二
(s = h.next) == null ||
//队列中老二线程是不是当前线程?
s.thread != Thread.currentThread()
);
}

357

被折叠的 条评论
为什么被折叠?



