当前位置: 首页 > 后端技术 > Java

AbstractQueuedSynchronizer公平锁非公平锁

时间:2023-04-01 16:46:08 Java

AQSjava.util.concurrent.locks.AbstractQueuedSynchronizer,翻译为抽象队列同步器AQS提供了一个简单的框架,用于原子管理同步状态、阻塞和唤醒线程函数、等待队列模型;AQS包含了一个虚拟的Node双向链表(即Waiting队列),由volatile修饰的head&tail节点,以及同步状态标志state,节点等待状态标志waitStatus,当前线程信息AQS提供了对共享锁和排他锁的支持独占锁可以一次只能被一个线程占用,比如ReentrantLock、ReentrantWriteLock等,其中也包括公平锁和非公平锁。共享锁可以被多个线程同时占用,比如ReentrantReadLock、Semaphore等公平锁&非公平锁初始化//fairlockReentrantLocklock=newReentrantLock(true);//非公平锁ReentrantLocklock=newReentrantLock();ReentrantLock锁=newReentrantLock(false);加锁过程java.util.concurrent.locks.ReentrantLock.FairSync#lockfinalvoidlock(){//尝试将当前锁状态设置为1,表示获取锁成功acquire(1);}publicfinalvoidacquire(intarg){/***尝试获取锁iftryAcquire=true,则获取锁成功*获取锁失败时进入后续等待进程acquireQueued*acquireQueued会无限循环获取锁*如果获取到锁,则直接开始执行*或者之前的节点成为头部,暂时执行*/if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))/***进入这个分支时*当前线程已经获取到锁*准备执行*但因为其他线程中断了本线程,所以这里响应中断*/selfInterrupt();}1.尝试加锁(公平锁模式)/***TryAcquiringlocksettinglockstatelockstate的值其实就是锁线程获取锁的次数(可重入)*加锁成功返回true,加锁成功返回falsefailure*/protectedfinalbooleantryAcquire(intacquires){//当前线程finalThreadcurrent=Thread.currentThread();//获取当前锁状态intc=getState();//如果当前锁状态为0,则可以加锁if(c==0){/***非公平锁模式不会判断队列中是否还有其他节点,会直接尝试获取锁*hasQueuedPredecessors=false*那么等待队列中没有其他线程*即当前线程是下一个应该获取锁的线程*/if(!hasQueuedPredecessors()&&/***考虑到其他线程已经完成了同时判断*所以进行CAS操作尝试修改锁状态*/compareAndSetState(0,acquires)){//如果前面两步成功,则将当前线程设置为持有锁的线程setExclusiveOwnerThread(current);返回真;}}//如果当前锁状态不为0且持有锁的线程为当前线程,则直接重新进入elseif(current==getExclusiveOwnerThread()){//设置当前锁状态+1,即当前线程被锁定的总次数intnextc=c+acquires;//如果锁数为负数,则锁数越界if(nextc<0)thrownewError("Maximumlockcountexceeded");设置状态(下一步);返回真;}返回假;}判断等待队列中是否还有其他线程(非公平锁没有这一步)publicfinalbooleanhasQueuedPredecessors(){//是否正确取决于head在tail之前被初始化//head.next是否准确当前//线程在队列中排在第一位。节点t=尾部;//以相反的初始化顺序读取字段Nodeh=head;节点;/***队头元素不等于队尾元素*并且队头之后的元素不为空或者队头之后的第一个等待线程不是当前线程*表示队列中至少有其他线程在等待*/returnh!=t&&((s=h.next)==null||s.thread!=Thread.currentThread());}锁状态变化的CAS操作受保护的最终布尔值compareAndSetState(intexpect,intupdate){//请参阅下面的内部函数设置以支持此返回unsafe.compareAndSwapInt(this,stateOffset,expect,update);}2.锁定失败并进入等待队列/***构建当前线程等待队列的元素为Node*下面会用Node来表示当前线程构造的等待队列元素*将等待Node的线程设置为持有当前锁的线程*CAS操作尝试将当前节点设置为队列的尾部元素*/privateNodeaddWaiter(Nodemode){Nodenode=newNode(Thread.currentThread(),mode);//尝试enq的快速路径;在节点pred=tail发生故障时备份到完整enq;//判断当前尾元素是否为空if(pred!=null){//尾元素不为空将尾元素设置为当前线程的前端节点元素Nodenode.prev=pred;//尝试将当前线程Node设置为尾元素if(compareAndSetTail(pred,node)){//成功后,将旧尾元素的最后一个节点修改为当前Nodepred.next=node;返回节点;}}enq(节点);返回节点;}finalbooleanacquireQueued(finalNodenode,intarg){booleanfailed=true;尝试{布尔中断=false;//当前Node会在这里无限循环,直到前一个节点成为head并自己获取锁后返回for(;;){//获取当前Node的前一个节点finalNodep=node.predecessor();//如果当前节点的前一个节点已经成为队头,则再次尝试加锁if(p==head&&tryAcquire(arg)){//成功后,将当前Node设置为队头元素setHead(节点);p.next=null;//helpGC//此时已经获取到了锁,所以不需要将自己设置为中断状态//入队结果为failed=false;返回中断;}/***shouldParkAfterFailedAcquire判断当前线程是否可以挂起*清空队列中取消的节点*如果为真,则可以挂起*/if(shouldParkAfterFailedAcquire(p,node)&&/***parkAndCheckInterrupt执行挂起*并返回当前线程是否有中断请求*/parkAndCheckInterrupt())/***当前线程在等待过程中不能响应中断,直到获取到锁*如果在整个等待过程中都被中断,则interrupted=true*acquireQueued最终返回true,否则返回false*并在外层响应中断*其他线程需要调用当前线程的interrupt()方法*/interrupted=true;}}finally{if(failed)cancelAcquire(node);}}解锁过程publicfinalbooleanrelease(intarg){//尝试解锁if(tryRelease(arg)){//解锁成功Nodeh=head;//获取当前头节点如果头不为空且等待状态不为0,则唤醒if(h!=null&&h.waitStatus!=0)unparkSuccessor(h);返回真;}returnfalse;}TrytounlockprotectedfinalbooleantryRelease(intreleases){//由于每次解锁都可以重试,实际上减少了当前线程的锁持有量intc=getState()-releases;//当前线程不是持锁线程异常抛出错误if(Thread.currentThread()!=getExclusiveOwnerThread())thrownewIllegalMonitorStateException();布尔自由=假;if(c==0){//如果锁状态为0,则当前线程不再持有锁,变为可锁free=true;//将锁持有者设置为空setExclusiveOwnerThread(null);}套泰特(c);免费退货;}唤醒头节点privatevoidunparkSuccessor(Nodenode){/**如果状态为负(即,可能需要信号)尝试*清除预期的信号。如果这个*失败或者状态被等待线程改变了也没关系。*///获取节点状态intws=node.waitStatus;if(ws<0)//CAS操作将节点状态设置为0,即初始化compareAndSetWaitStatus(node,ws,0);/**要取消停放的线程在后继者中举行,通常*只是下一个节点。但如果取消或明显为空,*从尾部向后遍历以找到实际*未取消的后继者。*///获取头节点A的下一个节点Nodes=node.next;//如果节点为空或者status大于0,说明节点已经被取消if(s==null||s.waitStatus>0){s=null;//从队尾开始向前移动元素,直到找到离头节点最近的节点for(Nodet=tail;t!=null&&t!=node;t=t.prev)if(t.waitStatus<=0)s=t;}//头节点的下一个节点(或者距离头节点最远的节点)close)不为空时唤醒本节点if(s!=null)LockSupport.unpark(s.thread);}参考:https://blog.csdn.net/Java_zh...https://blog.csdn.net/Leon_Ji...https://blog.csdn.net/yy_dieg...