alt 点击进入release()函数

alt

这个函数是一个AQS的方法,这些方法是父类实现的,不同得是加锁,解锁这些操作。这种方式体现了模板设计模式。

他会尝试去释放锁,释放成功会通知下一个等待者;

接着看tryRelease()干了什么工作:这个数字是加锁时候的一个反向操作的数字,由于线程重入概念的存在,并且多次的获得锁,会实现线程维护的资源状态数字增加,所以重入几次锁就需要释放几次锁 alt

获取当前线程和正在执行的线程不是一个线程就抛出异常;

定义了一个free,如果c==0表示资源被释放,就把free变为true,设置独占拥有者线程为null,加锁的时候是将这个值设置为当前的线程;

最后设置资源的状态为0,c只是一个局部变量; 于是就返回了true;于是release()函数进入了一个唤醒线程的工作;首先获取head,如果头为空,那么就直接返回了true就说明不用唤醒线程了;否则就头结点不为0;由于之前的修改,保证了节点是一个singal状态,所以头节点的ws一定不为0满足;于是就把继任者线程唤醒了;

unparkSuccessor()函数源码: alt alt

由于传入的是一个头结点,先获取了头节点的ws,通过cas方式把ws变为0,以前是singal,说明此节点不在监控下一个节点了。接着拿到下一个节点,如果节点是空的或者节点的ws是>0的就说明取消了等待;接着循环首先定义一个尾巴,尾巴等于前一个节点;判断p不等于node,并且p不等于null(node是一个如果不为头,并且不为空,相当于从双向链表的尾巴找到了头出),每次循环判断p节点ws是否小于等于0;

(简言之就是由于原先的头结点在之前的操作过程中保证了ws是-1(singal),所以使用这个函数一定会将这个头结点采用cas设置为0,意味着让头节点不在参与下一个线程的唤醒,于是就使用了s指向下一个节点,由于这个节点可能是空的,或者ws是1,那么就让s=null,但是这个队列中可能还是会有其他的线程,于是就用p节点从队列的尾巴依次遍历到头结点(不包含头结点),以及不包含null节点,只要每次改变的p所指节点的ws<=0,就会让节点指向队列中的第一个ws小于0的节点。并且设置s为此节点,而s节点就是我们想要唤醒的线程;所以就会执行最后一个判断语句)