简介
Semaphore用于控制访问资源的数量,可以理解为限流。比如数据库的最大连接数;
Semaphore是共享锁,支持公平锁与非公平锁
内部类Sync继承AQS,实现了获取、释放资源逻辑
基本组成
- permits
初始化时传入,代表允许访问资源的最大数量,会设置到AQS.state
- Sync
同步对象,继承AQS,实现共享锁对应的方法:tryAcquireShared、tryReleaseShared
- FairSync
公平的同步对象,继承Sync
- NonfairSync
非公平的同步对象,继承Sync
方法
- acquire
/**
* Semaphore#acquire
* 获取资源,失败会阻塞
*/
public void acquire() throws InterruptedException {
// 调用AQS模版方法
sync.acquireSharedInterruptibly(1);
}
- tryAcquireShared
/**
* FairSync#tryAcquireShared 公平锁获取资源
*/
protected int tryAcquireShared(int acquires) {
for (;;) {
// 判断当前线程是否有前节点,true代表有
if (hasQueuedPredecessors())
return -1;
int available = getState();
// 剩余资源数 = AQS.state - 本次需要获取的资源数
int remaining = available - acquires;
// 剩余资源数<0 或 剩余资源数<0时,CAS设置剩余资源数到AQS.state成功,返回剩余资源数
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
- nonfairTryAcquireShared
/**
* Sync#nonfairTryAcquireShared 非公平锁获取资源
* 与公平锁区别就是没有AQS#hasQueuedPredecessors判断
*/
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
- release
public void release() {
// 调用AQS#releaseShared,模版方法
sync.releaseShared(1);
}
- tryReleaseShared
/**
* Sync#tryReleaseShared
* 通过自旋+CAS,更新最新的数量 = 当前+释放
*/
protected final boolean tryReleaseShared(int releases) {
for (;;) {
// 获取当前AQS.state
int current = getState();
// 下一个数量 = 当前数量 + 释放的数量
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
// CAS更新AQS.state
if (compareAndSetState(current, next))
return true;
}
}
问题
- Hystrix使用到了Semaphore了吗?
Demo
@Slf4j
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 20; i++) {
new Thread(new Task(semaphore), String.valueOf(i)).start();
}
}
static class Task implements Runnable {
private Semaphore semaphore;
public Task(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
log.info("线程: {},准备获取资源", Thread.currentThread().getName());
semaphore.acquire();
log.info("线程: {},获取到资源", Thread.currentThread().getName());
Thread.sleep(5000);
log.info("线程: {},资源获取完毕,准备释放", Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
}
// 输出
线程: 2,准备获取资源
线程: 1,准备获取资源
线程: 0,准备获取资源
线程: 2,获取到资源
线程: 0,获取到资源
线程: 6,准备获取资源
线程: 5,准备获取资源
线程: 9,准备获取资源
线程: 3,准备获取资源
线程: 8,准备获取资源
线程: 4,准备获取资源
线程: 7,准备获取资源
线程: 0,资源获取完毕,准备释放
线程: 2,资源获取完毕,准备释放
线程: 1,获取到资源
线程: 6,获取到资源
线程: 6,资源获取完毕,准备释放
线程: 1,资源获取完毕,准备释放
线程: 9,获取到资源
线程: 5,获取到资源
线程: 5,资源获取完毕,准备释放
线程: 9,资源获取完毕,准备释放
线程: 3,获取到资源
线程: 8,获取到资源
线程: 8,资源获取完毕,准备释放
线程: 3,资源获取完毕,准备释放
线程: 4,获取到资源
线程: 7,获取到资源
线程: 7,资源获取完毕,准备释放
线程: 4,资源获取完毕,准备释放