例子
Semaphore的使用也非常简单,上面的列子,一个简单程序为
public static void main(String[] args) {
// 假设餐厅有20张椅子
Semaphore semaphore = new Semaphore(20 , true);
Random random = new Random();
// 10是假设单位时间的单子量
for (int i=0; i<10; i++){
Thread t = new Thread(){
@Override
public void run() {
try {
for (;😉{
// count是这个单子的客人要占用多少把椅子
int count = random.nextInt(9) + 1;
System.out.println(getName() + " need " + count + " chairs.");
// 为客人安排椅子
semaphore.acquire(count);
System.out.println(getName() + " 进食,占用 " + count + " chairs.");
// 安排上了,假设了进食时间
sleep(1000);
System.out.println(getName() + " 离开 。");
// 空出来椅子
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))
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】点击:Android架构视频+BAT面试专题PDF+学习笔记即可获取!
8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)即可获取!**