SynchronousQueue核心源码分析
一、SynchronousQueue的介绍
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue支持公平性和非公平性2种策略来访问队列。默认是采用非公平性策略访问队列。公平性策略底层使用了类似队列的数据结构,而非公平策略底层使用了类似栈的数据结构。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合传递性场景。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。
下面是SynchronousQueue类的类图,下文中将详细分析这种阻塞队列的实现细节:
二、SynchronousQueue类中重要的成员变量以及Transferer方法
SynchronousQueue重要的成员变量
//表示运行当前程序的平台,所拥有的CPU数量
static final int NCPUS = Runtime.getRuntime().availableProcessors();
//为什么需要自旋这个操作?
//因为线程 挂起 唤醒站在cpu角度去看的话,是非常耗费资源的,涉及到用户态和内核态的切换...
//自旋的好处,自旋期间线程会一直检查自己的状态是否被匹配到,如果自旋期间被匹配到,那么直接就返回了
//如果自旋期间未被匹配到,自旋次数达到某个指标后,还是会将当前线程挂起的...
//NCPUS:当一个平台只有一个CPU时,你觉得还需要自旋么?
//答:肯定不需要自旋了,因为一个cpu同一时刻只能执行一个线程,自旋没有意义了...而且你还站着cpu 其它线程没办法执行..这个
//栈的状态更不会改变了.. 当只有一个cpu时 会直接选择 LockSupport.park() 挂起等待者线程。
//表示指定超时时间的话,当前线程最大自旋次数。
//只有一个cpu 自旋次数为0
//当cpu大于1时,说明当前平台是多核平台,那么指定超时时间的请求的最大自旋次数是 32 次。
//32是一个经验值。
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
//表示未指定超时限制的话,线程等待匹配时,自旋次数。
//是指定超时限制的请求的自旋次数的16倍.
static final int maxUntimedSpins = maxTimedSpins * 16;
//如果请求是指定超时限制的话,如果超时nanos参数是< 1000 纳秒时,
//禁止挂起。挂起再唤醒的成本太高了..还不如选择自旋空转呢...
static final long spinForTimeoutThreshold = 1000L;
SynchronousQueue使用了一个非常关键的方法来转移数据(从生产者线程转移到消费者线程),下面是这个方法
abstract static class Transferer<E> {
/**
* @param e 可以为null,null时表示这个请求是一个 REQUEST 类型的请求
* 如果不是null,说明这个请求是一个 DATA 类型的请求。
*
* @param timed 如果为true 表示指定了超时时间 ,如果为false 表示不支持超时,表示当前请求一直等待到匹配为止,或者被中断。
* @param nanos 超时时间限制 单位 纳秒
*
* @return E 如果当前请求是一个 REQUEST类型的请求,返回值如果不为null 表示 匹配成功,如果返回null,表示REQUEST类型的请求超时 或 被中断。
* 如果当前请求是一个 DATA 类型的请求,返回值如果不为null 表示 匹配成功,返回当前线程put的数据。
* 如果返回值为null 表示,DATA类型的请求超时 或者 被中断..都会返回Null。
*
*/
abstract E transfer(E e, boolean timed, long nanos);
}
三、非公平策略的核心类TransferStack
1、TransferStack是SynchronousQueue的一个内部类,可以使用它实现非公平的访问队列。下面是其重要的成员变量以及一些公共方法
/** 表示Node类型为 请求类型(消费者) */
static final int REQUEST = 0;
/** 表示Node类型为 数据类型(生产者) */
static final int DATA = 1;
/** 表示Node类型为 匹配中类型
* 假设栈顶元素为 REQUEST-NODE,当前请求类型为 DATA的话,入栈会修改类型为 FULFILLING 【栈顶 & 栈顶之下的一个node】。
* 假设栈顶元素为 DATA-NODE,当前请求类型为 REQUEST的话,入栈会修改类型为 FULFILLING 【栈顶 & 栈顶之下的一个node】。
*/
static final int FULFILLING = 2;
//判断当前模式是否为 匹配中状态
static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }
static final class SNode {
volatile SNode next; // next node in stack
//与当前node匹配的节点
//null:还没有任何匹配 等于自己:表示当前为取消状态 等于别的Snode:当前为匹配状态
volatile SNode match; // the node matched to this
//假设当前node对应的线程 自旋期间未被匹配成功,那么node对应的线程需要挂起,挂起前 waiter 保存对应的线程引用,
//方便 匹配成功后,被唤醒。
volatile Thread waiter; // to control park/unpark
//数据域,data不为空 表示当前Node对应的请求类型为 DATA类型。 反之则表示Node为 REQUEST类型。
Object item; // data; or null for REQUESTs
int mode;
// Note: item and mode fields don't need to be volatile
// since they are always written before, and read after,
// other volatile/atomic operations.
SNode(Object item) {
this.item = item;
}
//CAS方式设置Node对象的next字段
boolean casNext(SNode cmp, SNode val) {
//优化:cmp == next 为什么要判断?
//因为cas指令 在平台执行时,同一时刻只能有一个cas指令被执行。
//有了java层面的这一次判断,可以提升一部分性能。 cmp == next 不相等,就没必要走 cas指令。
return cmp == next &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
/**
* 尝试匹配
* 调用tryMatch的对象是 栈顶节点的下一个节点,与栈顶匹配的节点。
*
* @return ture 匹配成功。 否则匹配失败..
*/
boolean tryMatch(SNode s) {
//条件一:match == null 成立,说明当前Node尚未与任何节点发生过匹配...
//条件二 成立:使用CAS方式 设置match字段,表示当前Node已经被匹配了
if (match == null &&
UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
//当前Node如果自旋结束,那么会使用LockSupport.park 方法挂起,挂起之前会将Node对应的Thread 保留到 waiter字段。
Thread w = waiter;
//条件成立:说明Node对应的Thread已经挂起了...
if (w != null) { // waiters need at most one unpark
waiter = null;
LockSupport.unpark(w);
}
return true;
}
return match == s;
}
/**
* Tries to cancel a wait by matching node to itself.
*/
void tryCancel() {
//match字段 保留当前Node对象本身,表示这个Node是取消状态,取消状态的Node,最终会被强制移除出栈。
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
//如果match保留的是当前Node本身,那表示当前Node是取消状态,反之 则 非取消状态。
boolean isCancelled() {
return match == this;
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long matchOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = SNode.class;
matchOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("match"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
//表示栈顶指针
volatile SNode head;
//设置栈顶元素,同样使用到了优化
boolean casHead(SNode h, SNode nh) {
return h == head &&
UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
}
/**
* Creates or resets fields of a node. Called only from transfer
* where the node to push on stack is lazily created and
* reused when possible to help reduce intervals between reads
* and CASes of head and to avoid surges of garbage when CASes
* to push nodes fail due to contention.
*
* @param s SNode引用,当这个引用指向空时,snode方法会创建一个SNode对象 并且赋值给这个引用
* @param e SNode对象的item字段
* @param next 指向当前栈帧的下一个栈帧
* @param mode REQUEST/DATA/FULFILLING
*/
static SNode snode(SNode s, Object e, SNode next, int mode) {
if (s == null) s = new SNode(e);
s.mode = mode;
s.next = next;
return s;
2、TransferStack转移的核心方法transfer
E transfer(E e, boolean timed, long nanos) {
//包装当前线程的Node
SNode s = null; // constructed/reused as needed
//e == null 条件成立:当前线程是一个REQUEST线程。
//否则 e!=null 说明 当前线程是一个DATA线程,提交数据的线程。
int mode = (e == null) ? REQUEST : DATA;
//自旋
for (;;) {
//h表示栈顶指针
SNode h = head;
/*
* If apparently empty or already containing nodes of same
* mode, try to push node on stack and wait for a match,
* returning it, or null if cancelled.
*/
//CASE1:当前栈内为空 或者 栈顶Node模式与当前请求模式一致,尝试着把当前node入栈并且等待一个匹配,最后会返回对应的数据,
// 如果这个节点在这过程中被取消了,则返回null
if (h == null || h.mode == mode) { // empty or same-mode
//条件一:成立,说明当前请求是指定了 超时限制的
//条件二:nanos <= 0 , nanos == 0. 表示这个请求 不支持 “阻塞等待”。 queue.offer();
if (timed && nanos <= 0) { // can't wait
//条件成立:说明栈顶处于取消状态,协助栈顶出栈,再一次自旋尝试。
if (h != null && h.isCancelled())
casHead(h, h.next); // pop cancelled node
elss
return null;
}
//什么时候执行else if 呢?
//当前栈顶为空 或者 模式与当前请求一致,且当前请求允许阻塞等待。
//snode(s, e, h, mode),将当前的信息包装为一个Snode节点
//casHead(h, s = snode(s, e, h, mode)) 入栈操作。
else if (casHead(h, s = snode(s, e, h, mode))) {
//执行到这里,说明 当前请求入栈成功。
//入栈成功之后要做什么呢?
//在栈内等待一个好消息,等待被匹配!
//awaitFulfill 等待被匹配的逻辑...
//1.正常情况:返回匹配的节点
//2.取消情况:返回当前节点 s节点进去,返回s节点...
SNode m = awaitFulfill(s, timed, nanos);
//条件成立:说明当前Node状态是 取消状态...
if (m == s) { // wait was cancelled
//将取消状态的节点 出栈...
clean(s);
//取消状态 最终返回null
return null;
}
//执行到这里 说明当前Node已经被匹配了...
//条件一:成立,说明栈顶是有Node
//条件二:成立,说明 Fulfill 和 当前Node 还未出栈,需要协助出栈。
if ((h = head) != null && h.next == s)
casHead(h, s.next); // help s's fulfiller
//当前NODE模式为REQUEST类型:返回匹配节点的m.item 数据域
//当前NODE模式为DATA类型:返回Node.item 数据域,当前请求提交的 数据e
return (E) ((mode == REQUEST) ? m.item : s.item);
}
}
/*
* If apparently containing node of complementary mode,
* try to push a fulfilling node on to stack, match
* with corresponding waiting node, pop both from
* stack, and return matched item. The matching or
* unlinking might not actually be necessary because of
* other threads performing action 3:
*
*/
//什么时候来到这??
//栈顶Node的模式与当前请求的模式不一致,会执行else if 的条件。
//栈顶是 (DATA Reqeust) (Request DATA) (FULFILLING REQUEST/DATA)
//CASE2:当前栈顶模式与请求模式不一致,且栈顶不是FULFILLING
else if (!isFulfilling(h.mode)) { // try to fulfill
//条件成立:说明当前栈顶状态为 取消状态,当前线程协助它出栈。
if (h.isCancelled()) // already cancelled
casHead(h, h.next); // pop and retry
// 条件成立:说明当前节点压栈成功,入栈一个 FULFILLING | mode NODE
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
//自旋,fulfill 节点 和 fulfill.next 节点进行匹配工作...
for (;;) { // loop until matched or waiters disappear
//m 与当前s 匹配节点。
SNode m = s.next; // m is s's match
// 如果m为null,说明除了s节点外的节点都被其它线程先一步匹配掉了
// 就清空栈并跳出内部循环,到外部循环再重新入栈判断
if (m == null) { // all waiters are gone
casHead(s, null); // pop fulfill node
s = null; // use new node next time
//回到外层大的 自旋中,再重新选择路径执行,此时有可能 插入一个节点。
break; // restart main loop
}
//什么时候会执行到这里呢?
//fulfilling 匹配节点不为null,进行真正的匹配工作。
//获取 匹配节点的 下一个节点。
SNode mn = m.next;
//尝试匹配,匹配成功,则将fulfilling 和 m 一起出栈
if (m.tryMatch(s)) {
//结对出栈
casHead(s, mn); // pop both s and m
//当前NODE模式为REQUEST类型:返回匹配节点的m.item 数据域
//当前NODE模式为DATA类型:返回Node.item 数据域,当前请求提交的 数据e
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match
// 尝试匹配失败,说明m已经先一步被其它线程匹配了
// 就协助清除它,进入下一个自旋,再一次进行尝试
s.casNext(m, mn); // help unlink
}
}
}
/*
If top of stack already holds another fulfilling node,
* help it out by doing its match and/or pop