1.Semaphore源码解析
1.1概述
- Semaphore信号量常用来做
限流
使用,在高并发场景下用于控制同一时刻对共享资源的访问。可以用于商品的秒杀等场景。 - Semaphore的内部是基于AQS的共享锁来实现的。使用了类似ReentrantLock的内部结构,
使用了内部类Sync(继承AQS)
,然后两个实现类NonfairSync(非公平逻辑)和FairSync(公平的逻辑)- 内部的资源使用的还是AQS的state (volatile int state)属性,当acquire()时,state会+1,当release()时,state - 1(常用的就是1,也可以指定)
1.2使用流程图
1.3内部结构
1.3.1静态抽象内部类Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
//构造方法 传入int值,赋值给内部的state。
Sync(int permits) {
setState(permits);
}
//获取state
final int getPermits() {
return getState();
}
//非公平模式下获取信号量 上来直接抢占锁,不会判断队列中是否有节点排队
final int nonfairTryAcquireShared(int acquires) {
//自旋 + CAS
for (;;) {
//获取当前的state
int available = getState();
//state - acquires 赋值给remaining
int remaining = available - acquires;
//判断减完之后的state < 0 那么直接返回一个负数,代表获取state失败
//条件二:state > 0 使用CAS方式尝试获取state,获取成功返回state的值,
//失败继续自旋获取。
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//CAS + 自旋 尝试将state的值加上releases。
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
//CAS + 自旋 将state的值减去reductions。
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
//CAS + 自旋 重置state为 0
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
1.3.2Sync实现类NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
// 构造方法,调用父类的构造方法
NonfairSync(int permits) {
super(permits);
}
/*
* @return ( > 0 表示获取信号量(锁)成功,< 0表示获取信号量失败)
*/
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
1.3.3Sync实现类FairSync(公平模式)
公平模式下,先检测同步队列中是否有排队的Node,如果有排队的Node直接进入同步队列排队,否则CAS更新state的值
。
1.3.4核心方法之acquire()
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// ||
// ||
// ||
// \/
//AQS的共享方法。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//当前线程标志位处于中断状态,直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//自旋 + CAS获取信号量,获取成功返回直接走业务逻辑,失败则构造节点入队阻塞。
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
/*
* CAS + 自旋尝试获取通行证,获取成功返回 >= 0的值,获取失败返回 < 0 的值。
*/
protected int tryAcquireShared(int acquires) {
//自旋
for (;;) {
/*
* 判断当前AQS阻塞队列内是否有等待者线程,如果有直接返回-1,表示当前acquire的操作 * 需要进入到等待队列
* (因为这里我们解析的是公平锁源码,所以这里会判断,非公平锁直接上来就抢)
*/
if (hasQueuedPredecessors())
return -1;
/*
* 执行到这里,有哪几种情况?
* 1.调用acquire时,AQS同步队列内没有其他等待者节点
* 2.当前节点 在同步队列中是headNext节点。
*/
//获取state的值,
int available = getState();
//remaining表示当前线程获取完信号量之后还剩余的信号量。
int remaining = available - acquires;
/*
* 条件一:remining < 0表示线程获取通行证失败
* 条件二:前置条件 remaining >= 0, 使用CAS更新state的值。
*/
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
/*
* 将当前线程构造为一个Node进入同步队列,并且
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//构造节点(共享模式)并入同步队列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
//自旋
for (;;) {
//获取前驱节点
final Node p = node.predecessor();
//当前节点时head.next节点,尝试获取信号量
if (p == head) {
//返回值表示的是state - arg 后的state值
int r = tryAcquireShared(arg);
//大于等于0,表示获取成功,然后
if (r >= 0) {
//设置当前节点为头节点,并进行唤醒后继节点的操作
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return;
}
}
/*
* 为当前Node在同步队列中找到一个状态合法的前驱Node,
* 然后挂起当前线程。
*/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1.3.5acquire简单流程图
1.3.6核心方法之release()
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
/*
* tryReleaseShared() CAS + 自旋方式释放资源,大概率都会成功
*/
if (tryReleaseShared(arg)) {
//唤醒获取资源失败的线程
doReleaseShared();
return true;
}
return false;
}
//此方法详细讲解见CountDownLatch源码解析
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
//唤醒head.next节点
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head)
break;
}
}