1 SynchronousQueue简述
这个队列中,不存储数据,而是存储生产者或者是消费者,当存储一个生产者到SynchronousQueue后,生产者会阻塞(具体根据调用的方法),生产者最终会有如下结果:
-
如果在阻塞期间有消费者来匹配,生产者就会将绑定的消息交给消费者
-
生产者等到阻塞结果,或者不允许阻塞,直接失败
-
生产者在阻塞期间线程被中断了,直接告辞
消费者与生产者的原理一样
生产者:
消费者:
方法同上
示例代码:
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<String>();
String msg ="你好!";
// 生产者
new Thread(()->{
boolean b = false;
try {
b = synchronousQueue.offer(msg,1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(b);
}).start();
Thread.sleep(100);
// 消费者
new Thread(()->{
System.out.println(synchronousQueue.poll());
}).start();
}
核心内部类:
abstract static class Transferer<E> {
// 这个方法就是生产者和消费者在调用读写数据时用到的核心方法
abstract E transfer(E e, boolean timed, long nanos);
}
生产者在调用上述方法时,第一个参数传递e,消费者在调用时,第一个参数传递null
Transferer存在两种实现方式:
-
TransferStack
-
TransferQueue
在构建SynchronousQueue时会指定使用哪种子类实现
public SynchronousQueue() {
this(false);
}
public SynchronousQueue(boolean fair) {
// 如果是公平,使用TransferQueue(队列结构,FIFO先进先出)
// 如果是非公平,使用TransferStack(栈结构,先进后出)
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
2 TransferQueue的源码分析
构建TransferQueue对象
TransferQueue() {
// 构建一个头尾节点都指向伪节点的队列,并设置isData属性为false
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
QNode(Object item, boolean isData) {
this.item = item;
// 区分消费者还是生产者
this.isData = isData;
}
put方法源码分析
public void put(E e) throws InterruptedException {
// 非空判断 抛出异常
if (e == null) throw new NullPointerException();
// 如果为null,则是由于线程中断导致的,直接抛出异常
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
offer方法源码分析
public boolean offer(E e) {
// 非空判断 抛出异常
if (e == null) throw new NullPointerException();
// 返回false,表示线程中断
return transferer.transfer(e, true, 0) != null;
}
offer(E e, long timeout, TimeUnit unit) 源码分析
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
// 非空判断 抛出异常
if (e == null) throw new NullPointerException();
// 如果为null,则是由于超时或线程中断导致的,否则直接返回true
if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
return true;
// 查看线程的中断标记位,如果不是中断,就是超时,返回false,否则抛出中断异常
if (!Thread.interrupted())
return false;
throw new InterruptedException();
}
transfer核心方法源码分析
// 核心方法实现
E transfer(E e, boolean timed, long nanos) {
// 构造生产者 或消费者对象
QNode s = null; // constructed/reused as needed
// true 代表生产者 否则代表消费者
boolean isData = (e != null);
// 因为没有加锁的操作,使用死循环 走大量的判断来避免并发问题
for (;;) {
// 将头尾节点的成员变量赋值给局部变量
QNode t = tail;
QNode h = head;
// 做了一个健壮性判断,如果为null 则说明TransferQueue没有初始化(可能发生了指令重排)
if (t == null || h == null) // saw uninitialized value
continue;
// 头节点等于尾节点:整个队列没有生产者也没有消费者
// 如果有节点,同时当前节点和队列节点属于同一种角色
// 以上两个情况均进入队列
if (h == t || t.isData == isData) {
// 拿到头节点的下一个节点
QNode tn = t.next;
//头节点不等于尾节点,说明有并发问题,重新走for循环
if (t != tail) // inconsistent read
continue;
// 头节点的后置节点不为null,说明有线程并发,添加了一个节点
if (tn != null) { // lagging tail
// 直接帮助那个线程修改tail指针指向,并继续执行for循环
advanceTail(t, tn);
continue;
}
// timed :false 无线阻塞,true 阻塞一段时间
// 如果timed为true 并且阻塞时间小于等于0,则返回null
if (timed && nanos <= 0L) // can't wait
return null;
// 如果可以阻塞,则将当前元素e构建为QNode节点,设置为生产者
if (s == null)
s = new QNode(e, isData);
// 基于CAS操作,将tail节点的next从null设置为当前的QNode
if (!t.casNext(null, s)) // failed to link in
// 如果进来,说明修改失败,重新执行for训话
continue;
// CAS操作成功,替换tail的指向
advanceTail(t, s); // swing tail and wait
// 如果进到队列了,则挂起线程,等生产者 或消费者
// x是返回替换后的数据
Object x = awaitFulfill(s, e, timed, nanos);
// 说明节点取消了
if (x == s) { // wait was cancelled
// 清空当前节点,跳过当前节点
clean(t, s);
return null;
}
// 判断当前节点是否还在队列中
if (!s.isOffList()) {
// 将当前节点设置为head
advanceHead(t, s); // unlink if head
// 如果拿到了数据,说明我是消费者
if (x != null) // and forget fields
// 将当前节点的item设置为自己
s.item = s;
// 置空线程
s.waiter = null;
}
return (x != null) ? (E)x : e;
} else {
// 匹配队列中的角色
// head的next作为要匹配的角色 // complementary-mode
QNode m = h.next; // node to fulfill
// 做并发判断,如果头节点、头节点的next节点、尾节点发生变化,则重新执行for循环
if (t != tail || m == null || h != head)
continue; // inconsistent read
// 没有并发问题,可以拿数据了
Object x = m.item;
//如果满足(isData == (x != null),说明出现了并发问题,消费者去匹配消费者,这不合理
if (isData == (x != null) ||
// 说明节点取消了,自己指向了自己
x == m ||
// 如果以上都不满足,说明可以CAS交换数据了
// 如果交换失败了,说明有并发问题
// 此时需要重新设置head节点,继续执行for循环
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
// 替换head
advanceHead(h, m);
// 唤醒head.next中的线程
LockSupport.unpark(m.waiter);
// 匹配好了,数据也交换了,直接返回
// 如果x不等于null,说明队列中是生产者,当前是消费者,直接返回x具体数据;
// 否则是队列是消费者,当前是生产者,直接返回自己的数据
return (x != null) ? (E)x : e;
}
}
}
transfer方法流程图