三十三、阻塞队列之SynchronousQueue

72 阅读6分钟

阻塞队列之SynchronousQueue

基本概念

SynchronousQueue队列与其他阻塞队列有很大区别,它是不存储数据的,存储的是生产者或者消费者

使用SynchronousQueue队列,生产者与消费者是直接进行消息传递的

生产者在生产数据时的情况:

  • 调用非阻塞方法时,如果队列中没有消费者,那么生产者直接返回,消息直接丢失
  • 调用阻塞方法时,生产者会等待消费者一段时间,过了时间还没有消费者,也是直接返回,消息直接丢失

消费者在消费数据时的情况

  • 调用非阻塞方法时,如果队列中没有生产者,那么消费者直接返回
  • 调用阻塞方法时,消费者会等待生产者一段时间,过了时间还没有生产者,也是直接返回

基本属性

image.png

内部抽象类Transferer:

  • Transferer类中有个transfer方法,无论生产者还是消费者都会调用这个方法,这个方法是实现SynchronousQueue功能的核心方法
  • 内部类TransferStack和TransferQueue是Transferer类的实现
  • TransferStack数据结构是栈,数据是后进先出的
  • TransferQueue数据结构是队列,数据是先进先出的

TransferStack类的基本属性:

image.png

SNode是封装数据的内部类,基本属性:

// 指向下一个节点
volatile SNode next;  
volatile SNode match;  
// 存放节点对应的线程  
volatile Thread waiter;   
// 存放数据
// 生产者有数据
// 消费者为null
Object item;   
int mode;

TransferQueue类的基本属性:

image.png

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方法

  1. 判断入参e是否为空,并将判断后的布尔值赋值给isData
  2. 如果isData为true,说明e不为空,说明是生产者;反之是消费者
  3. 进入for死循环
  4. 如果头节点为null或者尾节点为null,说明队列还未初始化完成,执行continue
  5. 在队列初始化完成情况下,如果队列中没有元素,或者元素类型与当前线程元素类型相同
  6. 断当前线程是否可以等待,如果不可以,直接返回;如果可以,是否已经超过等待时间
  7. 如果已经超过等待时间,也是直接返回;如果没有,将数据封装成QNode添加到队列的尾节点
  8. 当前线程阻塞一段时间
  9. 线程被唤醒后,判断item值是否是QNode,如果是,说明节点是被取消的,将节点从队列中删除,直接返回
  10. 如果不是,判断节点是否还在队列中,如果不是,返回数据
  11. 如果是,将当前节点设置成head节点,将节点的waiter属性设置为null,再返回数据
  12. 如果在队列初始化完成情况下,队列中有元素,并且元素类型与当前线程元素类型不同
  13. 获取队列head节点的next节点,判断节点是否已经被其他线程匹配上
  14. 如果是,将此节点设置成head节点,然后执行continue
  15. 如果不是,将此节点设置成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;
		}
	}
}