SynchronousQueue
基本介绍
- SynchronousQueue和其它阻塞队列最大的区别就是,SynchronousQueue存储数据的方式不同,它将数据封装在Node中,另外还存储了线程信息
- 从SynchronousQueue存储消息的特点可以决定,线程在存储或者取数据的时候,线程必须等待消息匹配成功才能做其它的事情。
- SynchronousQueue提供了两种Node存储数据,一种是基于队列的结构,遵循先进先出的原则;另一种是基于栈的结构,先进后出
- SynchronousQueue的核心功能是去匹配生产者和消费者,消费者Node直接从消费者Node中取数据
- SynchronousQueue保证线程安全是基于CAS操作完成的,并没有使用Lock锁,因此性能比其它阻塞队列更高
- 常用的方法(添加数据):
- offer():生产者在添加数据时,如果有消费者在等待消息,则匹配成功,即添加数据成功;如果没有消费者等待消息,则匹配失败,即添加失败。
- offer(time,unit):生产者在添加数据时阻塞一段时间,在指定时间内:如果有消费者在等待消息,则匹配成功,即添加数据成功;如果没有消费者等待消息,则匹配失败,即添加失败。
- put():生产者在添加数据时,如果有消费者等待消息,直接匹配,添加成功;如果没有消费者等待消息,则一直等待,直到有消费者消费消息。
- 获取数据的方法和添加数据方法相同
基本使用
构造方法中可以传入一个boolean值,如果为true使用队列是数据结构,如果为false默认使用栈的数据结构,无参构造默认false
public class SynchronousQueueTest {
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<String> queue = new SynchronousQueue<>(true);
// SynchronousQueue<String> queue = new SynchronousQueue<>();
for (int i = 0; i < 3; i++) {
int finalI = i;
Thread.sleep(10);
new Thread(() -> {
try {
queue.put("生产者线程" + finalI);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
Thread.sleep(10);
for (int i = 0; i < 3; i++) {
int finalI = i;
Thread.sleep(10);
new Thread(() -> {
try {
System.out.println("消费者线程" + finalI + "获取消息:" + queue.take());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
使用栈的数据结构结果:
消费者线程0获取消息:生产者线程2
消费者线程1获取消息:生产者线程1
消费者线程2获取消息:生产者线程0
使用队列的数据结果:
消费者线程0获取消息:生产者线程0
消费者线程1获取消息:生产者线程1
消费者线程2获取消息:生产者线程2
从结果可以看出,栈结构后插入的数据被先消费;队列结构先添加的数据被先消费
源码分析
成员变量
只需关心transferer变量,所有的存取数据的方法最终都是调用了transferer对象的transfer方法
//这个变量是指定存储的数据机构是基于队列存储还是基于栈存储
private transient volatile Transferer<E> transferer;
构造方法
//调用有参构造默认传入false
public SynchronousQueue() {
this(false);
}
//如果为true基于队列存储,如果false基于栈存储,默认使用栈存储
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
抽象静态内部类Transferer
只定义了一个抽象方法transfer
abstract static class Transferer<E> {
/**
*
* @param e 存储的元素,如果为消费者则传入null
* @param timed 是否阻塞等待
* @param nanos 阻塞时间的纳秒值
* @return
*/
abstract E transfer(E e, boolean timed, long nanos);
}
TransferQueue实现类
基于队列存储,遵循先进出原则
static final class TransferQueue<E> extends Transferer<E> {
//队列的头节点
transient volatile QNode head;
//队列的尾节点
transient volatile QNode tail;
//静态内部类,存储数据的Node
static final class QNode {
// 下一个node节点,可以看出是单向链表实现队列
volatile QNode next;
// 要存取的元素
volatile Object item;
// 存取数据的线程
volatile Thread waiter;
//标记是否是存数据
final boolean isData;
}
//构造方法,可以看出构建了一个尾节点
TransferQueue() {
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
//这个方法是基于队列形式存储数据的逻辑实现
E transfer(E e, boolean timed, long nanos) {
//省略……
}
}
TransferStack实现类
基于栈存储,遵循先进后出原则
static final class TransferStack<E> extends Transferer<E> {
static final class SNode {
//当前节点下个节点的node
volatile SNode next;
//和当前节点可以匹配的生产者节点或消费者节点
volatile SNode match;
//当前线程
volatile Thread waiter;
//存储的数据,如果为消费者则为null
Object item;
int mode;
}
//============只有一个默认的无参构造=======
//这个方法是基于栈形式存储数据的逻辑实现
E transfer(E e, boolean timed, long nanos) {
//………………
}
}
分析TransferQueue的transfer方法
/**
* e:元素内容,如果消费者,则为null
* timed:是否阻塞等待
* nanos:等待时间的纳秒值
*/
E transfer(E e, boolean timed, long nanos) {
QNode s = null;
//判断是否是生产者
boolean isData = (e != null);
//死循环
for (;;) {
//拿到队列的尾节点和头节点
QNode t = tail;
QNode h = head;
//防止对象还没创建好的校验
if (t == null || h == null) // saw uninitialized value
continue; // spin
//头尾节点相等或者头节点和当前节都是生产者或都是消费者,则进行队列插入操纵
if (h == t || t.isData == isData) { // empty or same-mode
//获取头节点的下一个节点
QNode tn = t.next;
//头节点发生变化,证明有其它线程改变了队列,下次for循环
if (t != tail) // inconsistent read
continue;
//头节点的下一个节点存在了,证明有其它线程改变队列了,将tail节点替换为tn,下次for循环
if (tn != null) { // lagging tail
advanceTail(t, tn);
continue;
}
//返回结果1 -> 如果需要不需要等待或等待时间到了,返回
if (timed && nanos <= 0L) // can't wait
return null;
//如果可以阻塞且没有创建过节点对象,这里为了for循环只创建一次
if (s == null)
s = new QNode(e, isData);
//基于CAS将tail节点的next设置为当前线程节点,等待生产者生产数据或等待被消费,如果设置失败下次for循环
if (!t.casNext(null, s)) // failed to link in
continue;
//CAS插入成功,替换tail指向
advanceTail(t, s); // swing tail and wait
//如果插入队列中了,挂起线程,等待消费或被消费
Object x = awaitFulfill(s, e, timed, nanos);
//如果节点取消,清空当前节点,将引用断开
if (x == s) { // wait was cancelled
clean(t, s);
return null;
}
//判断当前节点是否还在队列中,
if (!s.isOffList()) { // not already unlinked
//将当前节点设置为head
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
//将当前节点item设置为自己
s.item = s;
//线程置为null
s.waiter = null;
}
//生产者返回当前节点元素,消费者返回匹配节点中的元素
return (x != null) ? (E)x : e;
}
//证明可以匹配
else { // complementary-mode
//拿到head的next作为要匹配的节点
QNode m = h.next; // node to fulfill
//tail节点变动或者head的下个节点为null或者head节点变动,证明并发了,下次for循环
if (t != tail || m == null || h != head)
continue; // inconsistent read
//如果没有并发问题,拿到nead的next节点中的元素
Object x = m.item;
//isData == (x != null)判断是否都是生产者或都是消费者,x == m是否取消节点,!m.casItem(x, e)表示交互数据失败------->并发校验
if (isData == (x != null) ||x == m ||!m.casItem(x, e)) {
//重新设置head节点,下次for循环
advanceHead(h, m);
continue;
}
//如果没有并发问题,替换head
advanceHead(h, m); // successfully fulfilled
//唤醒head的next节点中的线程
LockSupport.unpark(m.waiter);
//生产者返回当前节点元素,消费者返回匹配节点中的元素
return (x != null) ? (E)x : e;
}
}
}