因此会用CAS保障入队安全 。
private Node enq(final Node node) { //多次尝试 , 直到成功为止 for (;;) { Node t = tail; //tail不存在 , 设置为首节点 if (t == null) { if (compareAndSetHead(new Node()))tail = head; } else { //设置为尾节点 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }出队过程会发生什么?一旦有节点出队 , 说明有线程释放资源了 , 队头的等待线程可以开始尝试获取了 。
于是首节点的线程释放同步状态后 , 将会唤醒它的后继节点(next)
而后继节点将会在获取同步状态成功时将自己设置为首节点
注意在这个过程是不需要使用CAS来保证的 , 因为只有一个线程能够成功获取到同步状态
AQS详细资源获取流程1. tryAcquire尝试获取资源AQS使用的设计模式是模板方法模式 。
具体代码如下:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 发现中断过 , 则触发中断异常 selfInterrupt();}即AQS抽象基类AbstractQueuedSynchronizer给外部调用时 , 都是调的acquire(int arg)方法 。这个方法的内容是写死的 。而acquire中 , 需要调用tryAcquire(arg) , 这个方法是需要子类实现的 , 作用是判断资源是否足够获取arg个
(下面部分代码注释选自: (2条消息) AQS子类的tryAcquire和tryRelease的实现_Mutou_ren的博客-CSDN博客_aqs tryacquire )
ReentrantLock中的tryAcquire实现这里暂时只谈论一种容易理解的tryAcuire实现 , 其他附加特性的tryAcquire先不提 。
里面主要就做这几件事:
- 获取当前锁的资源数
- 资源数为0 , 说明可以抢 , 确认是前置节点是头节点 , 进行CAS试图争抢 , 抢成功就返回true , 并设置当前线程
- 没抢成功 , 返回false
- 如果是重入的 , 则直接set设置增加后的状态值 , 状态值此时不一定为0和1了
目的是创建一个等待节点Node , 并添加到等待队列
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; // 通过CAS竞争队尾 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } // 竞争队尾失败 , 于是进行CAS频繁循环竞争队尾 enq(node); return node; } private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node()))tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
经验总结扩展阅读
- 许运东婉萍是什么电视剧中的人物?
- 为什么女儿9岁了睡觉头上出汗
- 迎战高考的励志祝福文案
- 许舒贝是什么电视剧中的人物?
- 裘正宇是什么电视剧中的人物?
- 西厂雨化田是什么电影中的人物?
- Briefings in Bioinformatics-2021 知识图谱-生物信息学-医学顶刊论文:生物信息学中的图表示学习:趋势、方法和应用
- 苏简和黄凯迪是什么电视剧中的人物?
- 3步训练培养你心目中的小神童
- 艾莉是什么电视剧中的人物?