AQS原理与实战:从state变量看ReentrantLock、CountDownLatch、Semaphore的并发控制差异
概述
最近在看AQS这块的View宝典,对AQS中的state变量在不同组件中所代表的含义有点好奇,所以特此前来整理下并加深下自身印象。
1、什么是AQS
AQS(全称:AbstractQueuedSynchronizer)是多线程队列同步器,是JUC包下多个组件的底层实现,例如Lock、CountDownLatch、Semaphore等都用到了AQS。

1.1 核心属性


1.2 工作原理
AQS内部由两部分组成:
1、是被volatile关键字修饰的state变量作为竞态条件。
2、基于双向链表的FIFO的阻塞队列。
工作原理:多个线程通过对state共享变量进行修改来实现竞态条件,竞争失败的线程加入到FIFO队列并阻塞,抢占到竞态资源的线程释放资源之后,后续的线程按照FIFO的顺序有序唤醒。
2、Lock的state变量
首先Lock是一个接口,具体的实现类为ReentrantLock。其中ReentrantLock支持公平锁和非公平锁两种。
// 无参构造(默认采用非公平锁)
public ReentrantLock() {
sync = new NonfairSync();// 默认采用非公平锁
}
// 有参构造(可以自由指定,fair为true时采用公平锁;反之采用非公平锁)
public ReentrantLock(boolean fair) {
// 根据三目运算符 得到sync
sync = fair ? new FairSync() : new NonfairSync();
}

2.1 以NonfairSync为例
// 尝试获取锁
public final void acquire(int arg) {
// 如果tryAcquire返回false时,表示获取锁失败
// 此时会尝试将Node节点放入FIFO队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 尝试获取锁,此时acquires为1
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// 非公平尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 当前锁的持有次数
int c = getState();
if (c == 0) {
// 如果锁的持有次数为0,则表示当前锁已经被释放了,
// 通过CAS将当前锁占用,并设置被当前线程占用
// 最后返回true
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程等于占用锁的线程,那么则将锁的次数+1,
// 并且将新的state值(加1之后的值)进行设置,最后返回true
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 此处表示锁抢占失败了,返回false
return false;
}
2.2 小结
经过2.1源码分析可得,ReentrantLock中的state变量代表的是锁的可重入次数。
3、CountDownLatch的state变量
// 只有有参构造,且count值不能为负数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}

3.1 countDown()方法
public void countDown() {
// 锁释放减一
sync.releaseShared(1);
}
//
public final boolean releaseShared(int arg) {
// 当且仅当tryReleaseShared为true,即就是state值为0的时候才会返回true。
if (tryReleaseShared(arg)) {
// 此时关键代码是:LockSupport.unpark(s.thread);
// 表示唤醒当前被阻塞的线程,
// :unpark 不会立即唤醒线程,而是给线程发放一个许可(permit)。
doReleaseShared();
return true;
}
return false;
}

3.2 小结
经过3.1分析可得,CountDownLatch中的state变量代表的是等待的计数(倒数计时器)。
4、Semaphore的state变量
// 有参构造(默认非公平)
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
// 有参构造(通过fair指定公平还是非公平)
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

4.1 NonfairSync的acquire()方法
public void acquire(int permits) throws InterruptedException {
// permits不允许为负数
if (permits < 0) throw new IllegalArgumentException();
// 获取许可证
sync.acquireSharedInterruptibly(permits);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// 如果当前线程被阻塞时,此时会返回true
if (Thread.interrupted())
throw new InterruptedException();
// 尝试获取
if (tryAcquireShared(arg) < 0)
// 如果小于0,则将该线程通过LockSupport.park(this)挂起
doAcquireSharedInterruptibly(arg);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 获取可用的permit数量
int available = getState();
// 剩余的permits数量 = 当前可用的permit数量 - 获取所需的permit数量
int remaining = available - acquires;
// 如果remaining剩余permit小于0 或 remaining> 0 通过CAS将剩余permits设置成功
if (remaining < 0 ||
compareAndSetState(available, remaining))
// 返回剩余的permit许可证
return remaining;
}
}
4.2 小结
经过4.1分析可得,Semaphore中的state变量代表的是许可证的数量。
综上所述是我对AQS工作原理的理解以及核心组件中state变量所表示的含义,如果存在不对的地方也希望同学指正哦,最后也希望可以帮助到屏幕前的你,加油!!!

5095

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



