1. 条件队列的意义
Condition将Object监控器方法( wait , notify和notifyAll )分解为不同的对象,从而通过与任意Lock实现结合使用,从而使每个对象具有多个等待集。 Lock替换了synchronized方法和语句的使用,而Condition替换了Object监视器方法的使用。
条件(也称为条件队列或条件变量)为一个线程暂停执行(“等待”)直到另一线程通知某些状态条件现在可能为真提供了一种方法。 由于对该共享状态信息的访问发生在不同的线程中,因此必须对其进行保护,因此某种形式的锁与该条件相关联。 等待条件提供的关键属性是它自动释放关联的锁并挂起当前线程,就像Object.wait一样。
Condition实例从本质上绑定到锁。 要获取特定Lock实例的Condition实例,请使用其newCondition()方法
2. 条件队列原理 2.1 条件队列结构条件队列是一个单向链表,在该链表中我们使用nextWaiter属性来串联链表。但是,就像在同步队列中不会使用nextWaiter属性来串联链表一样,在条件队列是中,也并不会用到prev, next属性,它们的值都为null。
队列的信息包含以下几个部分:
private transient Node firstWaiter;// 队列的头部结点
private transient Node lastWaiter;// 队列的尾部节点
队列中节点的信息包含以下几个部分:
当前节点的线程 thread
当前节点的状态 waitStatus
当前节点的下一个节点指针 nextWaiter
结构图:
注意:
在条件队列中,我们只需要关注一个值即可那就是CONDITION。它表示线程处于正常的等待状态,而只要waitStatus不是CONDITION,我们就认为线程不再等待了,此时就要从条件队列中出队。
2.2 入队原理每创建一个Condtion对象就会对应一个Condtion队列,每一个调用了Condtion对象的await方法的线程都会被包装成Node扔进一个条件队列中
3. 条件队列与同步队列一般情况下,等待锁的同步队列和条件队列条件队列是相互独立的,彼此之间并没有任何关系。但是,当我们调用某个条件队列的signal方法时,会将某个或所有等待在这个条件队列中的线程唤醒,被唤醒的线程和普通线程一样需要去争锁,如果没有抢到,则同样要被加到等待锁的同步队列中去,此时节点就从条件队列中被转移到同步队列中
1. 条件队列转向同步队列图注意图中标红色的线
但是,这里尤其要注意的是,node是被 一个一个转移过去的,哪怕我们调用的是signalAll()方法也是一个一个转移过去的,而不是将整个条件队列接在同步队列的末尾。
同时要注意的是,我们在同步队列中只使用prev、next来串联链表,而不使用nextWaiter;我们在条件队列中只使用nextWaiter来串联链表,而不使用prev、next.事实上,它们就是两个使用了同样的Node数据结构的完全独立的两种链表。
因此,将节点从条件队列中转移到同步队列中时,我们需要断开原来的链接(nextWaiter),建立新的链接(prev, next),这某种程度上也是需要将节点一个一个地转移过去的原因之一。
同步队列是等待锁的队列,当一个线程被包装成Node加到该队列中时,必然是没有获取到锁;当处于该队列中的节点获取到了锁,它将从该队列中移除(事实上移除操作是将获取到锁的节点设为新的dummy head,并将thread属性置为null)。
条件队列是等待在特定条件下的队列,因为调用await方法时,必然是已经获得了lock锁,所以在进入条件队列前线程必然是已经获取了锁;在被包装成Node扔进条件队列中后,线程将释放锁,然后挂起;当处于该队列中的线程被signal方法唤醒后,由于队列中的节点在之前挂起的时候已经释放了锁,所以必须先去再次的竞争锁,因此,该节点会被添加到同步队列中。因此,条件队列在出队时,线程并不持有锁。
3. 条件队列与同步队列锁关系条件队列:入队时已经持有了锁 -> 在队列中释放锁 -> 离开队列时没有锁 -> 转移到同步队列
同步队列:入队时没有锁 -> 在队列中争锁 -> 离开队列时获得了锁
4. 实战用法