SynchronousQueue(简写为SQ)
- 同步队列中,读线程与写线程只有相互匹配时才能完成一次完整的读写操作。而读与写的匹配依赖于TransferQueue或TransferStack中的
transfer()
。 - SQ中重要的数据结构有TransferQueue和TransferStack,还有QNode。QNode里有isData,item,next等成员变量
- 全流程概要: 以put(Obj e)为例,我们放入e之后,transfer()首先根据e是否为空来判断e与TransferQueue中的QNode是否目的一致(目的指写或者读)。假设目的一致,就需要将以new QNode(e)加入到队列,否则就poll出对应的QNode来完成写读一致性操作。
- TransferQueue中
transfer(obj e)
解析- 主题是一个
for(;;)
循环,根据Queue中QNode的目的是否和当前操作一致来确定进入哪一个分支。 - 如果一致或队列为空的话。
- 首先检验是否有不一致读的情况,即其他线程对队列进行操作(这里是由cas操作来控制并发,所以存在线程不安全的隐患)。再判断拖尾现象,即当前tail不是真正tail(仍有next)。上述两种边界条件都会continue。
- 随后会将新构建的QNode s插入到tail后,然后调用
advanceTail()
来更新tail节点。- advanceTail(t, nt)函数将nt置为tail。但与之功能类似的advanceHead()会多一步操作,即在cas之后给old head加上
h.next=h
,从而避免garbage retention。
- advanceTail(t, nt)函数将nt置为tail。但与之功能类似的advanceHead()会多一步操作,即在cas之后给old head加上
- 更新完后,会调用awaitFulfill()函数等待队列中QNode被match到。这里也是一个基于自旋锁类似的设置。
- awaitFullfill()函数首先会设置一个spins变量,可以理解为自旋次数,不过次数的设置条件不是很了解。然后就是一个经典的
for(;;)
循环(在并发场景中这种格式的循环频频出现),在循环体内,首先就是一个判断current thread是否被interrupt的if,如果if成立,就会调用tryCancle函数来将QNode s取消。- tryCancel()解析
- 本方法多用于
awaitFullFill()
中(基于UNSAFE.compareAndSwapObject()
) - 如果一个QNode被tryCancel了,那么its item will ref itself.
- 本方法多用于
- tryCancel()解析
- awaitFullfill()函数首先会设置一个spins变量,可以理解为自旋次数,不过次数的设置条件不是很了解。然后就是一个经典的
- 上述if不成立的话,会判断s.item与tranfer的目标对象e是否相同,如果不同就会返回s.item。这里就会引出
transfer()
(line 696)函数中的一个if逻辑。然后会根据spins是否大于0,来continue for-loop,如果不成立就会park住当前的thread。(这里其实有一个锁升级的场景)if(x==s)
(line 696)这个条件是来源于如果s被cancle了,那么s.item就等于s,而返回值x等于s.item,所以x=s
- 如果4中if逻辑成立,那么会对queue进行clean,即clean掉QNode s。假设不成立,会判断s是否OffList,如果没有的话,就说明s没有unlink掉,那么我们需要将其unlink掉。
- OffList()是判断当前QNode的next是否指向它本身,如果指向它本身的话,说明被advanceHead了。
- 最后返回item
- 如果目的不一致
- 在过滤掉不一致的场景后,我们直接将head QNode(实际上是head.next,但为了方便理解,这里统一使用head QNode)给advanceHead掉,然后unpark head QNode对应的线程,并返回item值。
- 这里值得熟记的一点就是line 714对应的if逻辑,这里主要判断了目标是否被完成,head QNode有没有被cancle,以及有没有成功cas headNode(
Cas(head, head.next)
)。
- 这里值得熟记的一点就是line 714对应的if逻辑,这里主要判断了目标是否被完成,head QNode有没有被cancle,以及有没有成功cas headNode(
- 在过滤掉不一致的场景后,我们直接将head QNode(实际上是head.next,但为了方便理解,这里统一使用head QNode)给advanceHead掉,然后unpark head QNode对应的线程,并返回item值。
- 主题是一个