// 空出来椅子
semaphore.release(count);
}
} catch (Exception e){
}
}
};
t.setName("Thread --> " + i);
t.start();
}
}
程序将一直执行下去,不会漏单,也不会出现椅子占用数量大于20的情况。
AQS基础
Semaphore是一种共享锁,实现依赖于AQS。对于锁,包含两部分知识,一部分是如何加解锁,另一部分是把锁分配给谁。AQS解决了把锁分配给谁的问题,Semaphore就可以聚焦于如何加解锁上。
掌握AQS将使Semaphore如何运转变得非常简单,没掌握也不影响对于本文的理解。
AQS原理可以参考:一文了解AQS
这里,需要了解AQS是如何运转的:
- 当申请锁,即调用了与acquire()类似语义的方法时,AQS将询问子类是否上锁成功,成功则继续运行。否则,AQS将以Node为粒度,记录这个申请锁的请求,将其插入自身维护的CLH队里中并挂起这个线程
- 在CLH队列中,只有最靠近头节点的未取消申请锁的节点,才有资格申请锁
- 当线程被唤醒时,会尝试获取锁,如果获取不到继续挂起;获取得到则继续运行
- 当一个线程释放锁,即调用release()类似语义的方法时,AQS将询问子类是否解锁成功,有锁可以分配,如果有,AQS从CLH队列中主动唤起合适的线程,过程为2、3
- 如果需要等待条件满足再去申请锁,即调用了wait()类似语义的方法时,在AQS中表现为,以Node为粒度,维护一个单向等待条件队列,把Node所代表的线程挂起
- 当条件满足时,即调用了signal()类似语义的方法时,唤醒等待条件队列最前面的未取消等待的Node,执行1
- 子类可以维护AQS的state属性来记录加解锁状态,AQS也提供了CAS的方法compareAndSetState()抢占更新state
简要来说,AQS分配锁时,当前线程可能会被挂起,接着被唤醒继续尝试申请锁,重复此过程直到获取到锁或取消等待。从外部看,就如入口方法被阻塞并在未来被恢复了一样。
Sync
Seamphore以内部类Sync继承AQS,并完成AQS所需子类实现的方法语义,Seamphore使用Sync即可完成各项工作。Sync应答加解锁的方法分别为nonfairTryAcquireShared()与tryReleaseShared()。
final int nonfairTryAcquireShared(int acquires) {
for (;😉 {
// 读取当前剩余资源
int available = getState();
// 获取 acquires数量 后剩余多少资源
int remaining = available - acquires;
if (remaining < 0 /①/||
// CAS 更新状态,失败说明有竞争条件,重新进入循环
compareAndSetState(available, remaining))
// 告诉AQS结果,大于等于0说明加锁成功
return remaining;
}
}
acquires的数量表示当前的线程要获取的资源数量,针对于AQS,就可以理解为需要多少把锁。在①处,说明锁已经分配出去很多了,不够分配了,因此分配不成功。
protected final boolean tryReleaseShared(int releases) {
for (;😉 {
// 当前还剩下的锁
int current = getState();
// 解releases个锁后,剩余的锁数量
int next = current + releases;
if (next < current)
// 进到这里说明,某个获取到锁的线程释没有正确释放锁
throw new Error(“Maximum permit count exceeded”);
if (compareAndSetState(current, next))
// CAS 更新锁状态,失败就重新进入循环
// ②
// 告诉AQS释放锁成功,有更多的锁可以分配
return true;
}
}
②处,tryReleaseShared()总是释放成功是因为,除非使用方式不当,否则没有理由到时释放失败。而当出现这种情况时,应直接抛出异常,避免程序进一步地错误下去。
实例化时,需要指定锁的数量。
Sync(int permits) {
setState(permits);
}
不公平锁的实现
Semaphore默认为不公平锁,可以通过Semaphore的实例化参数,决定Semaphore是否公平。不公平锁的实现,以Sync的子类NonfairSync实现。
static final class NonfairSync extends Sync {
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
直接使用Sync的方法,不赘述。
公平锁的实现
公平锁以Sync的子类FairSync实现。
static final class FairSync extends Sync {
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;😉 {
// 如果AQS的CLH队列中,有代表线程在等待的Node在排队,直接告诉AQS加锁失败
if (hasQueuedPredecessors())
return -1;
// 当前可获取的锁数
int available = getState();
// 获取acquires个锁后,还剩下多少
int remaining = available - acquires;
if (remaining < 0 /与nonfairTryAcquireShared()的说明一样/||
compareAndSetState(available, remaining) /CAS更新锁数量,失败重新进入循环/)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。
不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?
我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。
我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
mg-aEsv2MGA-1712337354820)]
[外链图片转存中…(img-sfRSu5CQ-1712337354820)]