阻塞队列之SynchronousQueue
基本概念
SynchronousQueue队列与其他阻塞队列有很大区别,它是不存储数据的,存储的是生产者或者消费者
使用SynchronousQueue队列,生产者与消费者是直接进行消息传递的
生产者在生产数据时的情况:
- 调用非阻塞方法时,如果队列中没有消费者,那么生产者直接返回,消息直接丢失
- 调用阻塞方法时,生产者会等待消费者一段时间,过了时间还没有消费者,也是直接返回,消息直接丢失
消费者在消费数据时的情况:
- 调用非阻塞方法时,如果队列中没有生产者,那么消费者直接返回
- 调用阻塞方法时,消费者会等待生产者一段时间,过了时间还没有生产者,也是直接返回
基本属性
内部抽象类Transferer:
- Transferer类中有个transfer方法,无论生产者还是消费者都会调用这个方法,这个方法是实现SynchronousQueue功能的核心方法
- 内部类TransferStack和TransferQueue是Transferer类的实现
- TransferStack数据结构是栈,数据是后进先出的
- TransferQueue数据结构是队列,数据是先进先出的
TransferStack类的基本属性:
SNode是封装数据的内部类,基本属性:
// 指向下一个节点
volatile SNode next;
volatile SNode match;
// 存放节点对应的线程
volatile Thread waiter;
// 存放数据
// 生产者有数据
// 消费者为null
Object item;
int mode;
TransferQueue类的基本属性:
QNode是封装数据的内部类,基本属性:
// 指向下一个节点
volatile QNode next;
// 存放的数据
// 生产者有数据
// 消费者为null
volatile Object item;
// 节点对应的线程
volatile Thread waiter;
// 为true代表是生产者,为false代表是消费者
final boolean isData;
SynchronousQueue的构造函数:
public SynchronousQueue() {
// 默认使用的是TransferStack
this(false);
}
public SynchronousQueue(boolean fair) {
// fair为true,使用的是TransferQueue
// fair为false,使用的是TransferStack
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
TransferQueue的Transfer方法
- 判断入参e是否为空,并将判断后的布尔值赋值给isData
- 如果isData为true,说明e不为空,说明是生产者;反之是消费者
- 进入for死循环
- 如果头节点为null或者尾节点为null,说明队列还未初始化完成,执行continue
- 在队列初始化完成情况下,如果队列中没有元素,或者元素类型与当前线程元素类型相同
- 断当前线程是否可以等待,如果不可以,直接返回;如果可以,是否已经超过等待时间
- 如果已经超过等待时间,也是直接返回;如果没有,将数据封装成QNode添加到队列的尾节点
- 当前线程阻塞一段时间
- 线程被唤醒后,判断item值是否是QNode,如果是,说明节点是被取消的,将节点从队列中删除,直接返回
- 如果不是,判断节点是否还在队列中,如果不是,返回数据
- 如果是,将当前节点设置成head节点,将节点的waiter属性设置为null,再返回数据
- 如果在队列初始化完成情况下,队列中有元素,并且元素类型与当前线程元素类型不同
- 获取队列head节点的next节点,判断节点是否已经被其他线程匹配上
- 如果是,将此节点设置成head节点,然后执行continue
- 如果不是,将此节点设置成head节点,然后唤醒节点的线程,最后返回数据
// e:生产者的数据
// timed:为true表示可以等待一段时间
// nanos:表示等待的时间
E transfer(E e, boolean timed, long nanos) {
QNode s = null;
// isData为true表示是生产者
// isData为false表示是消费者
boolean isData = (e != null);
for (;;) {
// 获取尾节点
QNode t = tail;
// 获取头节点
QNode h = head;
// 做以下判断是为了在指令重排序导致队列还没有初始化的情况下
// 避免后续操作出现问题
if (t == null || h == null)
continue;
// 判断队列是否为空,或者存储的类型与当前线程相同
if (h == t || t.isData == isData) {
// -------进入存储生产者或者消费者的流程--------
// 获取尾节点的next指针
QNode tn = t.next;
if (t != tail)
// 出现并发情况,重新走for循环
continue;
if (tn != null) {
// 出现并发情况,尾节点已经变化了
// advanceTail方法是帮助其他线程修改尾节点
advanceTail(t, tn);
// 重新走for循环
continue;
}
if (timed && nanos <= 0)
// 已经超过等待时间,直接返回
return null;
if (s == null)
// 这里判断s为null时,才创建出新节点
// 这样不用每次for循环走到这里都创建个新节点了
s = new QNode(e, isData);
if (!t.casNext(null, s))
// CAS修改尾节点的next失败了,重新走for循环
continue;
// 将tail指向当前节点
advanceTail(t, s);
// 线程挂起。
// 生产者挂起等待消费者,消费者挂起等待生产者
// x是当前节点的item
// 节点取消时,节点的item会设置成节点本身
Object x = awaitFulfill(s, e, timed, nanos);
if (x == s) {
// 当前节点的item就是节点本身,说明节点被取消
// 节点脱离队列
clean(t, s);
return null;
}
// 判断当前节点是否还在队列中
if (!s.isOffList()) {
// 当前节点还在队列中,说明当前节点匹配上了
// 将当前节点设置为head
advanceHead(t, s);
if (x != null)
s.item = s;
s.waiter = null;
}
// x != null 说明是消费者匹配上了生产者,消费者获取到数据
// x == null 说明是生产者匹配上了消费者,生产者返回自己的数据
return (x != null) ? (E)x : e;
} else {
// -------------进入生产者与消费者的匹配流程-------------------
QNode m = h.next;
if (t != tail || m == null || h != head)
// 出现并发情况,重新走for循环
continue;
Object x = m.item;
if (isData == (x != null) ||
// 当节点被取消时,节点的item会指向其本身
x == m ||
// casItem方法是将head的next节点中的item值设置成当前节点的数据
// 如果当前节点是生产者,那么m就是消费者,如果替换成功,那么m的item就不为null
// 如果当前节点是消费者,那么m就是生产者,如果替换成功,那么m的item就为null
// isData == (x != null)这个条件为true说明出现并发情况
!m.casItem(x, e)) {
// 进入这里说明出现并发情况,其他线程先匹配上了,需要重新设置头节点
advanceHead(h, m);
continue;
}
// 生产者与消费者正常匹配上了
// 需要重新设置头节点
advanceHead(h, m);
// 唤醒线程
LockSupport.unpark(m.waiter);
// x != null 说明是消费者匹配上了生产者,消费者获取到数据
// x == null 说明是生产者匹配上了消费者,生产者返回自己的数据
return (x != null) ? (E)x : e;
}
}
}