SynchronousQueue 是一个零容量的阻塞队列,不存储任何元素。每个插入操作(put)必须等待对应的移除操作(take),反之亦然。它的实现依赖于线程的阻塞和唤醒机制(通过LinkedTransferQueue 管理等待的线程),但这些是线程调度层面的结构,而非数据元素的存储空间。
LinkedTransferQueue详细分析见:LinkedTransferQueue核心设计与高效并发实现-CSDN博客
SynchronousQueue 通过继承和扩展 LinkedTransferQueue,巧妙地实现了零容量同步队列:
- 借用核心机制:利用
DualNode、xfer、CAS操作等基础设施 - 扩展LIFO能力:在
Transferer中实现栈模式的xferLifo方法 - 简化接口语义:所有容量方法返回0,体现零容量特性
- 双模式支持:公平模式使用队列,非公平模式使用栈(LIFO,更容易,且满足局部性原理;默认不公平)
这种设计既复用了 LinkedTransferQueue 的成熟并发机制,又通过扩展实现了独特的同步传输语义。
核心结构设计
主要组件
public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private final transient Transferer<E> transferer; // 核心传输器
private final transient boolean fair; // 公平性策略
}
零容量特性实现
所有容量相关方法都返回固定值:
public boolean isEmpty() { return true; } // 始终为空
public int size() { return 0; } // 始终为0
public int remainingCapacity() { return 0; } // 无剩余容量
public boolean contains(Object o) { return false; } // 不包含任何元素
内部传输器 - Transferer
Transferer 是 LinkedTransferQueue 的扩展,专门用于支持 LIFO(栈)模式:
static final class Transferer<E> extends LinkedTransferQueue<E> {
final Object xferLifo(Object e, long ns) { ... } // LIFO传输逻辑
private void unspliceLifo(DualNode s) { ... } // LIFO清理逻辑
}
利用 LinkedTransferQueue 的核心能力
SynchronousQueue 充分利用了 LinkedTransferQueue 的 DualNode 双端节点设计:
- 数据节点 (
isData = true):生产者等待消费者 - 请求节点 (
isData = false):消费者等待生产者
核心传输方法
private Object xfer(Object e, long nanos) {
Transferer<E> x = transferer;
return (fair) ? x.xfer(e, nanos) : x.xferLifo(e, nanos);
}
策略选择:
fair = true:使用LinkedTransferQueue.xfer()- FIFO队列模式fair = false:使用Transferer.xferLifo()- LIFO栈模式
借用的关键能力
| LinkedTransferQueue 能力 | SynchronousQueue 用途 |
|---|---|
DualNode 双端节点机制 | 区分生产者/消费者等待节点 |
xfer() 核心传输方法 | 公平模式下的 FIFO 传输 |
cmpExHead/cmpExTail CAS操作 | 线程安全的头尾指针更新 |
await() 阻塞等待机制 | 线程阻塞与唤醒 |
matched() 匹配检测 | 判断节点是否已配对 |
LIFO 模式实现
final Object xferLifo(Object e, long ns) {
boolean haveData = (e != null);
Object m;
outer: for (DualNode s = null, p = head;;) {
// 1. 寻找互补节点进行匹配
while (p != null) {
if (isData == haveData) break; // 同类型,跳出寻找
if (p.cmpExItem(m, e) != m) { // 尝试匹配
// 匹配成功,唤醒等待线程
LockSupport.unpark(p.waiter);
break outer;
}
}
// 2. 无匹配时推入新节点并等待
if (s == null) s = new DualNode(e, haveData);
s.next = p;
if (p == cmpExHead(p, s)) {
m = s.await(e, ns, this, p == null || p.waiter == null);
}
}
return m;
}
该函数实现了栈式的双重匹配算法,核心思想是:
- 生产者和消费者直接在栈顶进行匹配
- 无匹配时将节点压入栈顶等待
- 匹配成功后弹出对应节点
逐段代码分析
1. 初始化和匹配循环
boolean haveData = (e != null);
Object m; // the match or e if none
outer: for (DualNode s = null, p = head;;) {
haveData:标识当前操作类型(生产/消费)p = head:从栈顶开始处理s:延迟创建的新节点
2. 栈顶节点处理
while (p != null) {
boolean isData; DualNode n, u; // help collapse
if ((isData = p.isData) != ((m = p.item) != null))
p = (p == (u = cmpExHead(p, (n = p.next)))) ? n : u;
已匹配节点清理:
- 检测不一致状态:
isData != (item != null)表示节点已被匹配 - 原子性地弹出已匹配节点,更新栈顶
- 关键优化:协助其他线程清理,减少竞争
匹配逻辑判断
else if (isData == haveData) // same mode; push below
break;
else if (p.cmpExItem(m, e) != m)
p = head; // missed; restart
else { // matched complementary node
Thread w = p.waiter;
cmpExHead(p, p.next);
LockSupport.unpark(w);
break outer;
}
三种情况处理:
- 同类型节点:无法匹配,跳出内循环准备入栈
- 匹配失败:并发冲突,重新从栈顶开始
- 匹配成功:弹出节点,唤醒等待线程,完成交换
节点入栈和等待
if (ns == 0L) { // no match, no wait
m = e;
break;
}
if (s == null) // try to push node and wait
s = new DualNode(e, haveData);
s.next = p;
if (p == (p = cmpExHead(p, s))) { //成功加入等待栈
if ((m = s.await(e, ns, this, // spin if (nearly) empty
p == null || p.waiter == null)) == e)
unspliceLifo(s); // cancelled
else if (m != null)
s.selfLinkItem();
break;
}
入栈等待流程:
- 立即模式检查:
ns == 0L时不等待直接返回 【匹配了会直接推出 outer 循环】 - 构建新节点:延迟创建,减少不必要的对象分配
- 栈顶插入:
s.next = p建立链接,CAS更新栈顶 - 等待匹配:调用
await方法,支持超时和中断 - 清理处理:取消时调用
unspliceLifo清理节点
操作流程分析
put/offer 操作流程
- 验证元素非空:
Objects.requireNonNull(e) - 调用传输方法:
xfer(e, timeout) - 等待匹配:如果没有消费者,生产者线程阻塞
- 成功传输:匹配到消费者时直接传递元素
take/poll 操作流程
- 调用传输方法:
xfer(null, timeout) - 寻找生产者:查找等待的生产者节点
- 获取元素:从生产者节点获取元素并唤醒生产者
- 返回结果:成功获取元素或超时返回null
设计优势与应用场景
核心优势
- 零开销存储:无内部缓冲区,内存效率极高
- 同步传输:确保生产者与消费者直接交互
- 公平性可选:支持FIFO公平模式和LIFO非公平模式
- 高性能:基于CAS操作,避免重锁
典型应用场景
- 线程池任务传递:
Executors.newCachedThreadPool()内部使用 - 生产者-消费者直接交换:需要同步handoff的场景
- CSP通信模式:类似Go语言channel的同步通信
import java.util.concurrent.SynchronousQueue;
public class SyncQueueExample {
public static void main(String[] args) {
SynchronousQueue<String> queue = new SynchronousQueue<>();
// 生产者线程
new Thread(() -> {
try {
String data = "Data-" + System.currentTimeMillis();
System.out.println("Producing: " + data);
queue.put(data); // 阻塞直到有消费者取走
System.out.println("Produced: " + data);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
Thread.sleep(1000); // 模拟处理延迟
String data = queue.take(); // 阻塞直到有数据可取
System.out.println("Consumed: " + data);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}


282

被折叠的 条评论
为什么被折叠?



