TransferQueue接口与常规的Queue实现不太一样,因此我把它单独拎出来做介绍。
1. 类结构图
图中的类/接口主要来自两个包。
java.util包
- Queue接口
- AbstractQueue抽象类
java.util.concurrent包(juc)
- BlockingQueue 阻塞队列接口
- TransferQueue 接口
- LinkedTransferQueue
- SynchronousQueue
其中,Queue、AbstractQueue、BlockingQueue在前文都有所提及,不再赘述。
2. TransferQueue(传输队列接口)
TransferQueue接口继承自BlockingQueue接口,因此有常规阻塞队列的所有操作。
区别于常规的阻塞队列,提供了一种适用于“生产者”、“消费者”模式的传输队列。考虑以下几个场景:
- 容器为空,消费者先到并因为当前无数据而阻塞,此时生产者到达并生产数据,从而消费者能消费数据,返回“传输成功(true)”;——tryTransfer(E e)
- 容器为空,生产者先到并尝试生产数据,但当前没有消费者阻塞等待数据,生产者立马放弃传输,返回“传输失败(false)”; ——tryTransfer(E e)
- 容器为空,此时生产者到达并尝试生产数据,由于当前没有消费者,生产者(有限时间内)阻塞。在有限时间内如果有消费者到达接收数据,返回“传输成功”,否则返回“传输失败”。——tryTransfer(E e, long timeout, TimeUnit unit)
- 容器为空,此时生产者到达并尝试生产数据,由于当前没有消费者,生产者(无限)阻塞,直到消费者到达并消费数据。 ——transfer(E e)
以上提到的容器都可以理解成队列,生产就是“入队操作”,消费就是“出队操作”。
本接口提供的方法主要就是针对以上几种场景,接口源码如下:
/**
1. 一种生产者等待消费者接收元素的阻塞队列。
2. tryTransfer(Object) 非阻塞
3. tryTransfer(Object,long,TimeUnit) 有限时间阻塞
4. transfer(Object) 无限阻塞
5. @since 1.7
*/
public interface TransferQueue<E> extends BlockingQueue<E> {
/**
* 如果已经有一个消费者在等待接收,则立即传送给定的元素;否则,不传送元素,返回false
*/
boolean tryTransfer(E e);
/**
* 传送元素到消费者,如果当前没有等待接收的消费者,无限等待
*/
void transfer(E e) throws InterruptedException;
/**
* 如果给定时间内,有消费者等待接收元素,则传送元素并返回true;否则,不传送元素,返回false
*/
boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;
/**
* 判断是否“至少有一个消费者在等待接收元素”
*/
boolean hasWaitingConsumer();
/**
* 获取当前正在等待元素的消费者数目
*/
int getWaitingConsumerCount();
}
3. LinkedTransferQueue基于链表的传输队列
3.1 原理分析
基于链表实现传输队列。链表中的每个节点主要由以下属性组成:
// 用作标记是否是生产者占用的数据(true),反之,为消费者线程占用,为false
final boolean isData;
// 如果是生产者占用(isData=true),表示实际数据;如果是消费者占用(isData=false),表示null
// 通过CAS设置
volatile Object item;
// 下一个节点
volatile Node next;
// 等待的消费者线程,如果是生产者占用,为null
volatile Thread waiter;
同时,节点有两种状态(“未匹配”和“已匹配”),对应的节点属性变化可以由下面的表格表示:
匹配前 | 匹配后 | |
---|---|---|
生产者节点 | isData=true, item!=null | isData=true, item=null |
消费者节点 | isData=false, item=null | isData=false, item=this |
借助节点的类型,如果当前节点与队列中节点类型一致(同为生产/消费),则执行入队操作;如果节点类型不一致,则执行出队操作,更新队首位置。
附上参考资料:
3.2 源码
由于所有的队列操作都由xfer核心方法实现,这里我只贴xfer的代码分析
/*
* 核心xfer方法的how参数可能的取值
*/
// 用于非阻塞的poll, tryTransfer
private static final int NOW = 0;
// 用于offer、put、add插入操作(由于无界,所以都不会阻塞)
private static final int ASYNC = 1;
// 用于无限阻塞的transfer、take
private static final int SYNC = 2;
// 用于有限时间阻塞的poll、tryTransfer
private static final int TIMED = 3;
/**
* 核心方法,用于实现所有的队列方法
* @param e 传输的数据或null值
* @param haveData 是否插入类操作,如果true, 参数e不能为空
* @param how 表示是哪类操作:NOW/ASYNC/SYNC/TIMED
* @param nanos 超时时间(纳秒)
*/
private E xfer(E e, boolean haveData, int how, long nanos) {
// haveData 为true,表示是插入类操作,必须保证e不为空,否则抛NPE
if (haveData && (e == null))
throw new NullPointerException();
// 将要添加的节点
Node s = null;
retry:
for (;;) { // restart on append race
// 现有节点遍历
for (Node h = head, p = h; p != null;) { // find & match first node
boolean isData = p.isData;
Object item = p.item;
// 确保节点为未匹配状态
if (item != p && (item != null) == isData) { // unmatched
// 节点与此次操作模式一致,无需执行匹配过程,跳出循环,进入添加节点操作
if (isData == haveData)
break;
// 匹配成功
if (p.casItem(item, e)) {
for (Node q = p; q != h;) {
Node n = q.next; // update by 2 unless singleton
// 更新head为匹配节点的next节点
if (head == h && casHead(h, n == null ? q : n)) {
// 将旧节点自连接
h.forgetNext();
break;
} // advance and retry
if ((h = head) == null ||
(q = h.next) == null || !q.isMatched())
break; // unless slack < 2
}
// 匹配成功,则唤醒阻塞的线程
LockSupport.unpark(p.waiter);
// 返回匹配节点的元素
return LinkedTransferQueue.<E>cast(item);
}
}
// 如节点已经是匹配状态,则遍历到下一个节点
Node n = p.next;
p = (p != n) ? n : (h = head); // Use head if p offlist
}
// 链表中所有节点无法匹配时会走到这里
if (how != NOW) {
// 新建节点s
if (s == null)
s = new Node(e, haveData);
// 追加新节点,如果失败,重试;如果成功
Node pred = tryAppend(s, haveData);
if (pred == null)
continue retry; // lost race vs opposite mode
// 生产数据都不会阻塞,所以如果当前节点是消费者占用,就要根据参数决定是否要阻塞
if (how != ASYNC)
return awaitMatch(s, pred, e, (how == TIMED), nanos);
}
return e; // not waiting
}
}
4. SynchronouseQueue同步队列
4.1 原理分析
SynchronouseQueue提供了两种策略方式:
- 非公平策略(默认) - 基于栈方式,LIFO后进先出。
- 公平策略 - 基于队列方式,FIFO先进先出。
SynchronouseQueue核心方法在于两个内部类对抽象方法 transfer(E e, boolean timed, long nanos)
的实现。
原理部分我觉得自己无法说的比参考博客清楚,所以这里偷懒附上链接,请自行食用:
4.2 源码
附上我本人在源码学习中的部分理解
/**
* 一种阻塞队列实现
* 无容量概念
* 入队操作必须等待其它线程的出队操作,反之,出队操作也必须等待其它线程的入队操作
* @since 1.5
* @author Doug Lea and Bill Scherer and Michael Scott
*/
public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/**
* 栈/队列的通用内部api(栈:入栈/出栈;队列:入队/出队)
* Shared internal API for dual stacks and queues.
*/
abstract static class Transferer<E> {
/**
* 入队/出队操作。
* 如果e为null,则表示无数据,数据从生产者传输给容器(队列)
* 如果e不为null,则表示有数据。数据从容器(队列)传输给消费者
*/
abstract E transfer(E e, boolean timed, long nanos);
}
// CPU核数
static final int NCPUS = Runtime.getRuntime().availableProcessors();
/**
* 有限时间阻塞前最大自旋次数
*/
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
/**
* 无限阻塞前最大的自旋次数
*/
static final int maxUntimedSpins = maxTimedSpins * 16;
/**
* The number of nanoseconds for which it is faster to spin
* rather than to use timed park. A rough estimate suffices.
*/
static final long spinForTimeoutThreshold = 1000L;
/** 非公平策略 - 栈方式 */
static final class TransferStack<E> extends Transferer<E> {
// 消费者占用节点标记
static final int REQUEST = 0;
// 生产者占用节点标记
static final int DATA = 1;
/** Node is fulfilling another unfulfilled DATA or REQUEST */
static final int FULFILLING = 2;
/** Returns true if m has fulfilling bit set. */
static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }
// 栈中的节点
static final class SNode {
// 下一个节点
volatile SNode next; // next node in stack
// 与当前节点匹配的节点,阻塞时为null
volatile SNode match; // the node matched to this
// 记录线程
volatile Thread waiter; // to control park/unpark
// 数据
Object item; // data; or null for REQUESTs
int mode;
...
}
// 栈顶元素
volatile SNode head;
...
/**
* 插入/获取一个元素
* 如果参数e为空,说明是消费者线程占用
* 如果参数e不为空,说明是生产者线程占用
*/
@SuppressWarnings("unchecked")
E transfer(E e, boolean timed, long nanos) {
/*
* 循环完成下面三个操作之一:
* 1. 如果当前栈为空,或者已经存在“同模式”(同属于生产者或消费者)的节点,则添加新节点
* 2. 如果当前栈中包含与本次操作“互补模式”的节点,则先入栈,然后两个元素(分别为生产、消费,顺序可能调换)一同出栈,返回对应的数据项
* 3. 如果当前栈顶是一个标记为“匹配”类型的节点,帮助它出栈。与2中出栈区别,此步骤并不返回对应的数据项
*/
SNode s = null; // constructed/reused as needed
// 模式(消费者REQUEST,生产者DATA)
int mode = (e == null) ? REQUEST : DATA;
for (;;) {
// 栈顶节点
SNode h = head;
// 对应上面的操作1(栈为空或者“同模式”),需要添加新节点
if (h == null || h.mode == mode) {
// 非阻塞方式
if (timed && nanos <= 0) {
// 如果栈顶元素已被取消,则执行出栈操作
if (h != null && h.isCancelled())
casHead(h, h.next);
else
return null;
} else if (casHead(h, s = snode(s, e, h, mode))) { // CAS方式入栈一个新节点
// 有限时间内阻塞节点s,看是否有“互补模式”的节点
SNode m = awaitFulfill(s, timed, nanos);
// 如果阻塞时间超时,仍没有“互补”节点,则执行节点取消,返回null
if (m == s) {
clean(s);
return null;
}
// 走到这里,表示栈中的前两个元素完成了一次“匹配”
// 将栈顶前两元素出栈,并设置上新的栈顶
if ((h = head) != null && h.next == s)
casHead(h, s.next);
// 如果当前线程是REQUEST-消费模式,则从“互补”的m(生产模式)中获取数据;否则,s才是生产模式的数据
return (E) ((mode == REQUEST) ? m.item : s.item);
}
} else if (!isFulfilling(h.mode)) { // 栈顶不是匹配状态的节点,且与当前操作“互补模式”
// 如果栈顶节点已经被“取消”,CAS执行出栈,栈指向下一元素
if (h.isCancelled())
casHead(h, h.next);
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { // 插入一个新的“互补模式”节点
// 循环直到节点匹配或者与s匹配的节点消失
for (;;) {
// 正常情况下,m就是s节点的匹配节点
SNode m = s.next;
// 如果m为空,表明栈中已经没有“互补模式”的节点,那么直接将栈顶指向null,表示空栈
if (m == null) {
casHead(s, null);
s = null;
break;
}
SNode mn = m.next;
// 节点s与节点m“互补模式”达到匹配,此时栈中同时将两者出栈,并将栈顶指向后续节点
if (m.tryMatch(s)) {
casHead(s, mn);
// 如果当前线程是REQUEST-消费模式,则从“互补”的m(生产模式)中获取数据;否则,s才是生产模式的数据
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match
s.casNext(m, mn); // help unlink
}
}
} else {// 对应上面描述中的操作3. 这里栈顶已经是匹配状态的节点
// 正常m就是栈顶h节点的“互补”节点
SNode m = h.next;
// 但是如果h是栈中仅有的匹配状态元素,栈中没有别的元素了,那么直接将栈顶指向null,表示空栈
if (m == null)
casHead(h, null);
else {
SNode mn = m.next;
// 协助完成“匹配”操作,并设置新的栈顶
if (m.tryMatch(h))
casHead(h, mn);
else
h.casNext(m, mn);
}
}
}
}
/**
* 线程自旋/阻塞,直到节点s完成匹配或者设置时间到期
*/
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
// 设置过期的时间点,0表示不阻塞方式
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
// 当前线程发生中断,对插入的节点执行“取消”操作
if (w.isInterrupted())
s.tryCancel();
SNode m = s.match;
// 节点s拿到了“互补模式”的节点m,直接返回
if (m != null)
return m;
// 有限时间阻塞(这里兼容了非阻塞方式)
if (timed) {
// 阻塞倒计时
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
// 线程阻塞已经超时,对插入的节点执行“取消”操作
s.tryCancel();
continue;
}
}
// 自旋操作
if (spins > 0)
spins = shouldSpin(s) ? (spins-1) : 0;
// 如果节点线程为null,设置上线程
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
// 无限阻塞
else if (!timed)
LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
...
}
/** 公平策略 - 队列方式 */
static final class TransferQueue<E> extends Transferer<E> {
/** Node class for TransferQueue. */
static final class QNode {
// 队列下一节点
volatile QNode next; // next node in queue
// 数据
volatile Object item; // CAS'ed to or from null
// 节点的线程
volatile Thread waiter; // to control park/unpark
// 标记是否数据节点(true表示生产者占用节点,false表示消费者占用节点)
final boolean isData;
...
}
// 队列头
transient volatile QNode head;
// 队列尾部
transient volatile QNode tail;
/**
* 处于“取消”的节点
*/
transient volatile QNode cleanMe;
TransferQueue() {
// 用虚节点作为队首
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
...
/**
* 插入/获取一个数据
*/
@SuppressWarnings("unchecked")
E transfer(E e, boolean timed, long nanos) {
/*
* 循环完成以下两种行为之一:
* 1. 如果队列为空,或者已经存在“同模式”(同属于生产者或消费者)的节点,则往队列中添加新节点
* 2. 如果队列中有“互补模式”的阻塞节点,CAS匹配并执行出队
*/
QNode s = null; // constructed/reused as needed
// 如果为true,表示是“生产者线程”;如果是false,表示是“消费者线程”
boolean isData = (e != null);
for (;;) {
QNode t = tail;
QNode h = head;
if (t == null || h == null) // saw uninitialized value
continue; // spin
// 对应情况1,需要进行入队操作
if (h == t || t.isData == isData) {
QNode tn = t.next;
if (t != tail) // inconsistent read
continue;
// 队尾的next不为空,CAS方式更新队尾指针
if (tn != null) {
advanceTail(t, tn);
continue;
}
// 非阻塞方式,直接返回null,而且不用执行节点入队
if (timed && nanos <= 0)
return null;
// 新建节点s
if (s == null)
s = new QNode(e, isData);
// CAS方式设置队尾节点的next为节点s
if (!t.casNext(null, s)) // failed to link in
continue;
// CAS更新队尾指针到节点s
advanceTail(t, s);
// 线程阻塞,等待“匹配”
Object x = awaitFulfill(s, e, timed, nanos);
// 节点s“取消”
if (x == s) { // wait was cancelled
clean(t, s);
return null;
}
if (!s.isOffList()) { // not already unlinked
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
s.item = s;
s.waiter = null;
}
return (x != null) ? (E)x : e;
} else { // 对应情况2
// 最先匹配的队首节点
QNode m = h.next;
if (t != tail || m == null || h != head)
continue; // inconsistent read
Object x = m.item;
// 如果队首元素已经是“匹配/取消”状态,挪动队首指针到m(即原队首h出队),并重复循环
if (isData == (x != null) ||
x == m ||
!m.casItem(x, e)) {
advanceHead(h, m);
continue;
}
// 成功匹配,执行h节点出队,队首指向下一个节点m
advanceHead(h, m);
LockSupport.unpark(m.waiter);
return (x != null) ? (E)x : e;
}
}
}
}
private transient volatile Transferer<E> transferer;
/**
* 构造SynchronousQueue,默认非公平方式
*/
public SynchronousQueue() {
this(false);
}
/**
* 根据给定的公平策略,构造SynchronousQueue
* 公平策略,基于TransferQueue
* 非公平策略,基于TransferStack
*/
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
...
}