AQS(AbstractQueuedSynchronizer)总结(哭了,看的我亚历山大)
https://www.cnblogs.com/waterystone/p/4920797.html
https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
https://javadoop.com/2017/06/16/AbstractQueuedSynchronizer/
https://www.javadoop.com/post/AbstractQueuedSynchronizer-2/
https://www.javadoop.com/post/AbstractQueuedSynchronizer-3
AQS主要是更改state的数值
有两种资源的共享模式exclusive(reentrantlock)和share(countdownlatch/Semaphore
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
reentrantlock
acquire -> if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// tryAcquire(arg)没有成功,这个时候需要把当前线程挂起,放到阻塞队列中。
tryacquire(arg) (获得锁 1.无人持有直接cas 2.有人拿了,看是否是同一个线程)
addWaiter(Node.EXCLUSIVE) 先是直接添加到阻塞队列的尾部(尾部不是空)使用cas
如果尾部为空或者cas出错,就用enq(node)添加(会先创建一个初始化的head(假如空的话),然后添加到他的后面)
acquireQueued(进入阻塞队列,判断是第一个还是其他位置)如果是第一个,若前驱是head,就使用tryacquire竞争一下锁
如果是其他位置,将前驱节点的waitstate设置为-1,如果大于0,就向前找小于等于0的waitstate,然后挂起
release->获得state,将state-1 如果state等于0就释放(将head的waitstate=0,从后向前找waitestate<=0的点,找到后唤醒(unpark(node)唤醒后的node从park的地方起来),再set一下state
reentranlock分为公平锁和非公平锁,非公平锁的当前线程调用lock的时候直接用cas抢锁,抢不到后和公平锁一样请求锁acquire
在调用tryacquire的时候又会使用cas抢,然后再和之前一样去阻塞队列排队
condition基于reentranlock实现,await和signal都要获得锁
conditon的await()响应中断->addcondition()将节点添加到条件队列,插入到尾部(先得到tail的node,清除非condition的状态节点(unlinkcancelledwaiter(普通单链表的操作)),然后在初始化node并添加到条件队列(入队了)
之后完全释放锁(直接把state=0,返回赋值前的值)->(isOnSyncQueue(判断条件是否是condition node.next!=null 并在阻塞队列中找时候有这个元素))如果都没有,等待进入阻塞队列(挂起)(其他线程可以来获得锁)
signal(要获得当前线程的独占锁)将要转移的node(一般是第一个,也有可能第一个取消了(通过cas判断waitstate))与条件队列断绝关系,使用enq加入阻塞队列(state会变成0),如果阻塞队列中node前驱节点放弃了锁或者cas前驱节点的waitstate(cas signal)失败了就唤醒unpark节点 node唤醒的情况 1.获得锁 2. 中断 3. 前面的情况
node被唤醒之后,会checkinterruptwhilewaiting 判断是否发生了中断而且如果发生中断是发生在signal前或者signal后(transferAfterCancelledwait 通过cas设置state判断),中断唤醒后如果发现不是被signal的(被signal的node waitestate会变成0,node通过cas检查)会主动加入到阻塞队列中enq
加入阻塞队列中,准备获得锁,acquirequeue(node,savedstate)并判断时候发生中断,有中断就进行处理(程序处理,例如抛出或者不抛)
countdownlatch
将state设为n,使用countdown() 减少1,state=0的时候就唤醒调用await()的方法
await()->tryacquire(判断state是否=0来返回1,-1) 返回-1->doacquiresharedinterruptibly 先入队,将前驱节点waitstate设置为-1,挂起(可以重复多次入队挂起 数量等于n)
countdown()->tryreleaseshared(不断减少state)如果=0,就调用doreleaseshared方法(state0)
第一个线程被doreleaseshared()里面的unparksuccess唤醒之后,由于state0,循环在第二轮的时候>tryacquire返回1,线程被调用setheadandProgate(node,r),设置为头结点(state为0)并唤醒下一节点调用doreleaseshared()
cyclicbarrier
基于condition实现,只有当每个线程await()了之后,使用generation开始下一次的新生,另外在执行过程中有中断 超时或者要做的action出现异常 栅栏就会break,所有await的线程会notifyall然后抛出异常
semephroe
分公平策略和非公平策略,state表示资源的数量,state=0表示需要等到release,其他的和共享的aqs差不多,通过判断资源的数量(cas)来阻塞线程