AQS的许可证颁发、利用AQS实现独占锁

共 10905字,需浏览 22分钟

 ·

2023-05-27 20:25









11-6 AQS的许可证颁发









AQS在Semaphore的应用






Semaphore中,state表示



许可证





剩余数量




11-6-1


Semaphore


源码









主要方法










Acquire

: 获取许可证







Release

: 释放许可证







acquire





public void acquire(int permits) throws InterruptedException {


if (permits < 0) throw new IllegalArgumentException();


sync.acquireSharedInterruptibly(permits);


}





acquireSharedInterruptibly









public final void acquireSharedInterruptibly(int arg)



throws InterruptedException {


if (Thread.interrupted())


throw new InterruptedException();


if (tryAcquireShared(arg) < 0)


doAcquireSharedInterruptibly(arg);


}





tryAcquireShared




有两种实现
, 公平和不公平









NonfairSync

extends


Sync

非公平情况







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;


}


}





主要作用
:


 获取许可证, 其返回值






正数

: 代表获取成功,

负数

: 获取失败





首先获取当前可用许可证的数量


int



available = getState();







其次计算当前可用许可证数量是否满足本次要求
,


acquires


想要获取许可证的数量






只要当前可用许可证数量

-

 想要获得的许可证数量

<

 0, 就会返回负数;





上一个调用该方法的方法就会将当前线程放入等待队列中等待






11-6-2


AQS在Semaphore的应用











Semaphore中,


state表示许可证的剩余数量










tryAcquire


()


方法,判断
nonfairTryAcquireShared大于等于0的话,代表成功







这里会先检查剩余许可证数量够不够这次需要的,用减法来计算,如果直接不够,那就返回负数,表示失败,如果够了,就用自旋
 
+
 

compareAndSetState


来改变
state状态,直到改变成功就返回正数;

 
或者是期间如果被其他人修改了

导致剩余数量不够了,那也返回负数代表获取失败






11-7 利用AQS实现独占锁









AQS在ReentrantLock的应用








分析释放锁的方法

tryRelease







11-7-1


ReentrantLock


 |


unlock




public void unlock() {


sync.release(1);


}




public final boolean release(int arg) {


if (tryRelease(arg)) {


Node h = head;


if (h != null && h.waitStatus != 0)


unparkSuccessor(h);


return true;


}


return false;


}



第一步就调用了


tryRelease



方法




protected final boolean tryRelease(int releases) {


int c = getState() - releases;


if (Thread.currentThread() != getExclusiveOwnerThread())


throw new IllegalMonitorStateException();


boolean free = false;


if (c == 0) {


free = true;


setExclusiveOwnerThread(null);


}


setState(c);


return free;


}



判断当前线程是不是持有锁的线程




Thread.currentThread() != getExclusiveOwnerThread()




只有持有锁的线程才能解锁
, 否则抛出异常








当前
state数量


 

-

 需要释放的数量

=

 
0
 的情况下才会去释放锁






int c = getState() - releases;


if (c == 0) {


free = true;


setExclusiveOwnerThread(null);


}




tryRelease


方法返回
true以后, 调用该方法者就会执行如下代码





Node h = head;


if (h != null && h.waitStatus != 0)


unparkSuccessor(h);


return true;



唤醒后续节点

unparkSuccessor







11-7-2


ReentrantLock


 |


lock







public void lock() {


sync.lock();


}


abstract void lock();




此处是一个抽象方法
, 没有被实现







因为根据公平
/非公平有两种锁的策略



4a7746ff56606cb46561618a36d6bc28.webp





以非公平为例




final void lock() {


if (compareAndSetState(0, 1))


setExclusiveOwnerThread(Thread.currentThread());


else


acquire(1);


}




首先进行
CAS操作







if (compareAndSetState(0, 1))




期望值是
0, 意味着当前没有任何人持有这把锁的时候, state为0的时候才能加锁成功






并且将当前线程设置为持有锁的线程




setExclusiveOwnerThread(Thread.currentThread())



另一个分支是

acquire







acquire




public final void acquire(int arg) {


if (!tryAcquire(arg) &&


acquireQueued(addWaiter(Node.EXCLUSIVE), arg))


selfInterrupt();


}



首先调用

tryAcquire









tryAcquire


实现在
ReentrantLock类中







非公平为例







protected final boolean tryAcquire(int acquires) {


return nonfairTryAcquire(acquires);


}




final boolean nonfairTryAcquire(int acquires) {


final Thread current = Thread.currentThread();


int c = getState();


if (c == 0) {


if (compareAndSetState(0, acquires)) {


setExclusiveOwnerThread(current);


return true;


}


}


else if (current == getExclusiveOwnerThread()) {


int nextc = c + acquires;


if (nextc < 0) // overflow


throw new Error("Maximum lock count exceeded");


setState(nextc);


return true;


}


return false;


}



判断该锁是否被持有






int c = getState()




c == 0

, 说明无人持有该锁, 通过CAS将该锁持有, 设置当前线程持有该锁






if (compareAndSetState(0, acquires)) {


setExclusiveOwnerThread(current);


return true;


}




c != 0


并且当前线程是该锁的持有者







else if (current == getExclusiveOwnerThread()) {


int nextc = c + acquires;


if (nextc < 0) // overflow


throw new Error("Maximum lock count exceeded");


setState(nextc);


return true;


}




则说明本次获取锁
, 对于当前线程来说是一个重入操作, 所以stat =


c + acquires







然后设置
stat值

 setState(nextc);






c != 0 并且当前线程


不是


该锁的持有者







则代表该锁已经被其他线程持有了
, 则获取失败







11-7-3 AQS在ReentrantLock的应用









分析

释放锁

的方法

tryRelease


()







由于是可重入的,所以

state代表重入的次数

,每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是就抛异常,如果是的话,重入次数就减
1

,如果减到了
0,就说明完全释放了,于是free就是true,并且把state设置为0。







加锁的方法










近期热文




参考资料




1 | JUC







责编

 | 小耶哥




本期作者

 | 小耶哥




平台建设及技术支持

 | 小耶哥














浏览 65
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报