AQS1---走向稳定态
AQS的思想(稳定思想):即使确定了正常节点,这个节点也可能下一秒异常,即使找到了正常节点,这个节点可能只是异常status=0/-1的节点,这些都不要紧,都只是在自己旋转‘生命周期’里面和自己所看到的 最大可能的保证队列的稳定状态,如果判断错了,其他节点会帮着处理直到队列真正的稳定。
AQS的思想(出队思想):通过head=-1出队,head=0出队失败(外部线程不会再次调用head出队)后面节点就要自行出队。(unlock方法会被没有进入队列抢到了锁的线程调用,队列中获取到锁的线程执行完之后也会调用)
队列走向稳定时候,是多个节点线程在调节某一个节点,并且还有一个出队的线程在调节head。
ReentrantLock#AQS是一个FIFO的队列,通过head来出队,通过taill入队。
- 队列稳定状态
- 队列处于没有稳定的中间态
- 队列稳定时候出队
- 队列没有稳定时候出队。
节点状态分为:正常status=-1/0,异常status=0/-1,异常status=1。Head节点一定是正常节点(status=0/-1,不可能=1,因为如果头结点获取锁后异常了,头节点.prev=null,在cancelAcquire方法里面执行while (pred.waitStatus > 0)就会抛出异常,整个代码都无法执行了)。区分节点是不是正常判断标准是status值(线程异常了只能通过节点的状态才能看出来,所以肯定有错误,都是尽可能的保证队列的稳定性,后面见到)。也就是说 异常status=0/-1跟正常节点是一样的,能够被作为前驱并且通过它也能够出队后面的节点(作为前驱不要紧,关键在于能不能像正常节点一样唤醒后面出队),因为head唤醒 异常status=0/-1节点时候跟唤醒正常节点一样能够再次去唤醒后面的节点(不会导致队列出不了队,异常status=0/-1节点线程里面的执行逻辑会自动判断去唤醒)。head唤醒 异常status=1节点时候可能不能够再次去唤醒后面的节点(因为status=1节点可能不会再去执行唤醒的代码)。
队列稳定状态(能够正常出队):
前驱:回路中前面status正常节点
后驱:回路中后面status正常节点
每2个正常节点构成前驱后继关系,两两相连是正常节点,其余不管,或者中间有异常节点:
达到稳定的中间状态:
调整都是以栅栏作为边界的,2个status正常节点中间不会再有statsu正常节点。每个节点都只保证自己前面的队列是稳定的,不管后面的。
Status正常不一定是正常节点,但是在做正常节点调整法的一定是正常节点,status异常一定是异常节点。
队列走向稳定状态时候:
真正正常节点调整方法:1找前驱status正常节点,2和这个节点建立后驱关系,2设置这个节点=-1。然后自己阻塞,自己阻塞之后前驱节点异常了不管了。
异常节点调整方法:1找前驱status正常节点 2设置自己=1 3帮助建立后驱关系 4自己.next=自己。 建立后驱关系之后前驱节点异常了不管了。
结论1:从前往后找,可能断开,如果不是因为自己.next=自己,那么从前往后也能找到所有的正常节点和部分异常节点。从后往前找,可以找到所有的status正常的节点,并且还会找到部分status异常节点。
结论2:真正正常节点‘生命周期’里面 开始旋转 到确定找到正常前驱那个时间点 再到阻塞,异常节点‘生命周期’里面 开始旋转到 确定正常前驱 再到退出,都是尽可能的建立正确的前驱后继关系 把队列变成稳定状态,自己阻塞了或者异常退出了 前驱后继关系不正确了 就不管了了,交给其他还在旋转的节点去处理。
AQS的思想(个人总结):即使确定了正常节点,这个节点也可能下一秒异常,即使找到了正常节点,这个节点可能只是异常status=0/-1的节点,这些都不要紧,都只是在自己旋转‘生命周期’里面和自己所看到的 最大可能的保证队列的稳定状态,如果判断错了,其他节点会帮着处理直到队列真正的稳定。
只有节点都阻塞了,异常节点都退出了,队列才是真正的稳定。
这些都是中间状态。
规律:
1 每个节点只受后面节点影响
2 prev只有自己修改,没有多线程问题
3 status正常的节点的next受到后面一个真正正常调整方法和多个异常节点调整方法修改指向后面真正正常节点,
4 Status异常的节点的next只有自己修改
5 status正常的节点的status受到后面一个真正正常和多个异常修改=-1。Status异常的节点的status只有自己修改=1
再看几张图:
如果多线程同时修改同一个对象的同一个属性,一个直接赋值一个使用cas赋值(并且CAS只赋值一次)当冲突时候,直接赋值的优先级最大,cas赋值的失败了就算了让步给直接赋值的。