当前位置:网站首页>AQS 之 Semaphore 源码分析
AQS 之 Semaphore 源码分析
2022-06-09 05:11:00 【smartjiang-java】
1:Semaphore 基础
信号量:用来限制能同时访问共享资源的线程上限。
和锁不一样,锁只有一个共享资源;信号量的共享资源有多个,对访问的线程上限做限制。
作用:
可以做限流,只适合单机版,只限制线程数量,而不是限制资源数(tomcat 连接数)
可以作用于数据库连接池,享元模式下的,性能和可读性更好
2:创建信号量
Semaphore semaphore = new Semaphore(n);
// 传入 int 数字,表示共享资源的数目
public Semaphore(int permits) {
// 进入NonfairSync(permits) 方法
sync = new NonfairSync(permits);
}
进入NonfairSync(permits) 方法
NonfairSync(int permits) {
// 调用父类的 NonfairSync(permits) 方法
super(permits);
}
调用父类的 NonfairSync(permits) 方法
Sync(int permits) {
// 将传入进来的数值设置成 state 的值,作为共享变量
setState(permits);
}
3:获取信号量
调用 acquire()方法
public void acquire() throws InterruptedException {
// 进入 acquireSharedInterruptibly(1)方法
sync.acquireSharedInterruptibly(1);
}
进入 acquireSharedInterruptibly(1)方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// 如果线程被打断,抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 进入 tryAcquireShared(1) 方法
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
进入 tryAcquireShared(1) 方法:
如果是公平锁,
protected int tryAcquireShared(int acquires) {
for (;;) {
// 如果等待队列中有线程,返回-1,非公平锁多了这一步的检查
if (hasQueuedPredecessors())
return -1;
// 获取 state 的数目
int available = getState();
// state -1
int remaining = available - acquires;
// 如果< 0 ,表示共享资源已经被使用完成;返回-1
// 如果 >0.cas 修改 state 的值,并返回
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
如果是非公平锁
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
回到 acquireSharedInterruptibly(1)方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared(1) 返回剩余 state 的值, >=0 表示获取成功,线程继续向下运行
//如果 < 0,进入 doAcquireSharedInterruptibly(1)方法,加入等待队列
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
进入 doAcquireSharedInterruptibly(1)方法
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
// 如果是第一个进入等待队列的线程,会先创建一个亚元线程作为头节点;当前线程节点加入等待队列作为尾节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
// 获得当前节点的前一个节点,且如果是头节点
final Node p = node.predecessor();
if (p == head) {
// 再次尝试获取锁
int r = tryAcquireShared(arg);
// 如果获取成功,当前节点设置为头节点
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// shouldParkAfterFailedAcquire ():如果获取锁失败,则将前一个节点的 waitStatus 从 0 变成 -1
// parkAndCheckInterrupt() :使当前线程阻塞。如果当前线程被唤醒,会再次进入 for 循环竞争
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
3:释放信号量
调用 release() 方法
public void release() {
// 进入 releaseShared(1) 方法
sync.releaseShared(1);
}
进入 releaseShared(1) 方法
public final boolean releaseShared(int arg) {
// 进入 tryReleaseShared(1)方法
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
进入 tryReleaseShared(1)方法
protected final boolean tryReleaseShared(int releases) {
for (;;) {
// 进入循环,获取当前 state 的值
int current = getState();
// state 数值加 1
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
// cas 替换 state 的值,返回 true
if (compareAndSetState(current, next))
return true;
}
}
回到 releaseShared(1) 方法
public final boolean releaseShared(int arg) {
// tryReleaseShared(1)返回 true
if (tryReleaseShared(arg)) {
// 进入 doReleaseShared() 方法
doReleaseShared();
return true;
}
return false;
}
进入 doReleaseShared() 方法
private void doReleaseShared() {
for (;;) {
Node h = head;
// 判断等待队列是否为空,且有除头节点以外的其他节点
if (h != null && h != tail) {
int ws = h.waitStatus;
// 判断头结点的 waitStatus 是否为 -1
if (ws == Node.SIGNAL) {
// cas 将头节点的 waitStatus 从 -1 变成 0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 进入 unparkSuccessor(h) 方法
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 队列为空,且只有头节点,返回
if (h == head) // loop if head changed
break;
}
}
进入 unparkSuccessor(h) 方法
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
// 头节点的 waitStatus 已经变成 0,不满足这个判断
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;
}
// 存在的话,被唤醒,在 doAcquireSharedInterruptibly() 方法的循环中继续尝试获取锁
if (s != null)
LockSupport.unpark(s.thread);
}
边栏推荐
- Listing of Yubang new material on Shenzhen Stock Exchange: market value of 4.7 billion, net profit deducted in the first quarter decreased by 18%
- Summary of Android Engineer interview experience with 5 years' work experience, summary of real interview questions of Ali + Tencent + byte jump
- The latest JMeter pressure test in the whole network is not much to say. I just want to teach you to use JMeter to write script pressure test as soon as possible
- Openstack Learning Series 12: installing CEPH and docking openstack
- wps ppt背景图片如何换颜色
- MarathonLb的负载研究
- Clcnet: Rethink integrated modeling with classified confidence network (with source code download)
- Typescript learning [5] type
- P1743 Audiophobia
- . Net core 3.0 grpc custom service
猜你喜欢

Load research of Marathon LB

ps如何给图像加白边

2022 welder (elementary) special operation certificate examination question bank and simulation examination

MQ消息丢失,消息一致性,重复消费解决方案

LRU cache

2022焊工(初级)特种作业证考试题库及模拟考试

Simulated 100 questions and answers of high voltage electrician examination in 2022

Product weekly report issue 29 | creation center optimization: the sending assistant adds the quality score detection function, and the blog adds the historical version of the content

Wuqi_ New progress in vision language navigation: pre training and sim2real

拉下新项目代码爆红
随机推荐
cksort
R language multivariable generalized orthogonal GARCH (go-garch) model for fitting and forecasting high-dimensional volatility time series of stock market
宇邦新材深交所上市:市值47亿 第一季扣非净利降18%
Latest list of 955 companies that do not work overtime (2022 Edition)
Load research of Marathon LB
爬取html入mysql插入失败
PS how to border an image
Types, advantages and disadvantages of MySQL indexes
2021 national vocational skills competition Liaoning "Cyberspace Security Competition" and its analysis (ultra detailed)
力扣今日题-1037. 有效的回旋镖
application. Properties mysql. cj. jdbc. Driver red
EF core uses scaffold dbcontext to generate model from database
Simulated 100 questions and answers of high voltage electrician examination in 2022
Camtasia studio2022 activation code serial number
Transformer里面的缓存机制
Ultimate shell - Zsh
记录一次将dmp文件导入oracle数据库(本地导线上),所遇到的问题及解决方法
2022焊工(初级)特种作业证考试题库及模拟考试
软键盘出现搜索
pytest_allure优先级、fixture-scope参数介绍