描述
SynchrounousQueue
是一个比较特殊的无界阻塞队列并支持非公平和公平模式,严格意义上来说不算一个队列,因为它不像其他阻塞队列一样能有容量,它仅有一个指向栈顶的地址,栈中的节点由线程自己保存。任意的线程都会等待直到获得数据(消费)或者交付完成(生产)才会返回。
SynchronousQueue
和普通的阻塞队列的差异类似于下图所示(非公平模式):
阻塞队列通常是存储生产者的生产结果然后消费者去消费,阻塞队列就类似于一个中转站。
SynchronousQueue
则存储生产结果,只告诉消费者生产者的位置,然后让其自己去与之交流(反过来一样),就没有中转的一个过程而是直接交付的。
SynchronousQueue
将数据交付的任务交给生产者或消费者自行处理,实现的非常看不懂。
那么既然是 Queue
,就可以通过 offer
和 take
方法来了解
offer:
public boolean offer(E e) { if (e == null) throw new NullPointerException(); return transferer.transfer(e, true, 0) != null; }
take:
public E take() throws InterruptedException { E e = transferer.transfer(null, false, 0); if (e != null) return e; Thread.interrupted(); throw new InterruptedException(); }
offer
和 take
中都调用了 transferer.transfer(...)
transferer
是一个接口 SynchrounousQueue
有两个实现类:
- TransferQueue: 用于公平交付
- TransferStack:用于不公平交付
这两个的作用可以通过SynchrounousQueue
的构造方法得知:
public SynchronousQueue(boolean fair) { transferer = fair ? new TransferQueue<E>() : new TransferStack<E>(); }
如果启用公平交付则创建 TransferQueue
否则使用 TransferStack
首先先分析 TransferStack.transfer
非公平的TransferStack
通过类名得知,是使用堆栈实现的,是一个LIFO的序列。
每个请求的线程都会被包装成一个 SNode
,具有以下属性:
class SNode { volatile SNode next; // 链接的下一个SNode volatile SNode match; // 与该线程匹配的另外一个线程SNode节点 volatile Thread waiter; // 当前请求的线程 Object item; int mode; //该节点的类型(模式) }
很显然,是一个链表结构,使用一个 mode
来标识该节点的类型,具有以下值:
// 代表一个消费者 int REQUEST = 0; // 代表一个生产者 int DATA = 1; // 代表已经和另外一个节点匹配 int FULFILLING = 2;
通过源码注释得知,整个 TransferStack.transfer
可以分为以下几步:
- 如果当前的栈是空的,或者栈顶与请求的节点模式相同,那么就将该节点作为栈顶并等待下一个与之相匹配的请求节点,最后返回匹配节点的数据(take或offer)或者null(被取消/中断)
- 如果栈不为空,请求节点与栈顶节点相匹配(一个是REQUEST一个是DATA)那么当前节点模式变为FULFILLING,然后将其压入栈中和互补的节点进行匹配,完成交付后同时弹出栈并返回交易的数据,如果匹配失败则与其他节点解除关系等带回收。
- 如果栈顶已经存在一个FULFILLING的节点,说明正在交付,那么就帮助这个栈顶节点快速完成交易。
下面用图来描述先生产后消费的例子
当栈为空将其封装为SNode
节点后入栈,自旋等待其他节点与自己匹配
这是TransferStack.transfer
的第一个部分,用来处理栈为空或者是多个生产者/消费者的情况,使得都自旋等待匹配。
if (h == null || h.mode == mode) { // empty or same-mode // 生产者/消费者不愿意等待则直接返回 if (timed && nanos <= 0L) { // can't wait if (h != null && h.isCancelled()) casHead(h, h.next); // pop cancelled node else return null; // 创建一个节点并将该节点作为栈顶 } else if (casHead(h, s = snode(s, e, h, mode))) { // 自旋等待下一个与之匹配的节点 SNode m = awaitFulfill(s, timed, nanos); // 如果该节点已经取消等待 if (m == s) { //清理该节点 clean(s); return null; } // 如果有节点与自己匹配那么就返回交换的元素 if ((h = head) != null && h.next == s) casHead(h, s.next); // help s's fulfiller // 如果当前模式为DATA就表示是 消费者等待生产者生产 // 如果当前模式为REQUEST就表示是 生产者等待消费者消费 return (E) ((mode == REQUEST) ? m.item : s.item); } }
栈不为空,消费者压入栈顶,消费者与生产者进行匹配,消费者改变头节点(也就是本身)的状态为 FuLFILLING
<img src="