SynchronousQueue 的 transfer:解析基于 Slot 的线程交替数据传递逻辑
SynchronousQueue的transfer方法通过无缓冲设计实现线程间直接数据传递。其核心是volatile修饰的Slot作为同步协调点,结合CAS操作保证原子性,以及park/unpark进行精准线程控制。生产者与消费者通过Slot交换数据或线程引用,完成“握手”。该方法支持公平与非公平两种模式,分别采用栈或队列组织等待线程,展现了高并发场景下零库
SynchronousQueue的transfer方法:无缓冲直接交接的线程握手艺术
在并发编程的世界里,有一种队列堪称“极简主义”的典范——它内部没有任何存储空间,却能让数据在生产者和消费者之间精准、原子地传递。这就是SynchronousQueue,而其灵魂所在,便是transfer方法。这套机制的核心,可以概括为一场精心设计的线程“碰头会”:双方必须同时到场,一手交数据,一手交凭证,完成一次完美的握手。整个过程高度依赖volatile Slot、CAS和park/unpark的精密协作,实现了线程间的精确同步与唤醒。

Slot 是什么:一个轻量级的同步协调点
首先得搞清楚,这里的Slot并非传统队列中的节点。它本质上是一个volatile修饰的AtomicReference字段,扮演着临时“中转站”或“信号灯”的角色。其工作流程非常清晰:
- 当一位消费者调用
take()却找不到现成的数据时,它不会空等。相反,它会尝试将自己的线程对象(或一个特殊的null标记)通过CAS操作写入Slot,然后立刻挂起自己,进入等待状态。 - 当一位生产者带着数据
put(x)到来时,如果发现Slot非空(这意味着已经有消费者在等了),它会直接将自己的数据x通过CAS替换进去,并随即唤醒那个等待的消费者。 - 反过来,如果生产者发现Slot是空的(没有等待者),它就会把自己包装成一个包含数据和线程引用的
QNode放入Slot,然后挂起,等待消费者的到来。
你看,Slot就像一个共享的公告板,谁先到谁就在上面留下自己的联系方式,后到的人看到后,要么直接完成交易,要么留下自己的信息然后等待。
transfer 如何实现线程交替:一次成功的“握手”流程
理论可能有些抽象,让我们看一个典型场景:生产者put(x)时,恰好遇到一位已经在等待的消费者take()。这场“握手”是如何一步步完成的?
- 第一步:消费者就位。 消费者已经执行了
slot.compareAndSet(null, this),成功将自己的线程引用注册到了Slot中,随后调用LockSupport.park(this)进入阻塞状态。 - 第二步:生产者抵达并交换。 生产者到达后,读取Slot发现不是null(而是一个线程引用),意识到有消费者在等。于是,它执行关键操作:
slot.compareAndSet(e, x),试图将Slot中的消费者线程引用原子性地替换成自己要传递的数据x。 - 第三步:唤醒对方。 CAS替换成功后,生产者立即调用
LockSupport.unpark(e)唤醒对应的消费者线程。 - 第四步:消费者验收。 消费者被唤醒后,会再次检查Slot当前的值。如果发现已经是数据x,则顺利取走并返回;如果发现值仍然是自己的线程引用(这可能是因为被中断或超时了),则会尝试通过CAS将Slot清空,然后处理中断或超时逻辑并退出。
整个过程行云流水,没有冗余的缓冲,完全是一次面对面的即时交易。
为什么需要 CAS + volatile + park/unpark 组合
如此精巧的设计,单靠任何一种技术都无法实现。它需要CAS、volatile和park/unpark三者紧密配合,各司其职:
- volatile Slot:保证可见性。 这是线程间能够“看见”对方的基础。它确保了生产者或消费者对Slot状态的修改,能立刻被另一个线程感知到,避免了因为线程缓存而导致“我到了,你却看不见”的尴尬局面。
- CAS 操作:保证原子性。 无论是注册等待、交换数据还是清理状态,这些关键步骤都必须原子完成。CAS防止了多个生产者同时尝试写入数据,或多个消费者同时尝试读取数据所造成的状态混乱和数据错乱。
- park/unpark:提供精准的线程控制。 相比传统的
wait/notify,park/unpark机制更加精准和可控。它允许线程在指定的对象上挂起和唤醒,并且不易产生“虚假唤醒”问题,这使得线程状态判断的逻辑更加健壮可靠。
这三者缺一不可,共同构筑了transfer方法正确、高效的基石。
公平模式下的 TransferStack vs 非公平模式的 TransferQueue
有趣的是,SynchronousQueue内部提供了两种不同的等待线程组织策略,对应着公平与非公平的模式:
- TransferQueue(非公平模式,默认): 其内部是一个FIFO(先进先出)的双向队列。新来的等待线程总是插入队尾,唤醒时则从队头开始。这实际上形成了一种“先到先得”的公平调度。我们常说的“非公平”指的是默认选择,但其内部队列本身是公平的。
- TransferStack(公平模式): 在构造函数中传入
true时启用。它采用LIFO(后进先出)的栈结构。这意味着后到达的线程可能会优先获得匹配机会。这种策略的初衷是减少线程的平均等待时间,提升整体吞吐,但潜在的代价是可能导致早到的线程被“饿死”。 - 需要明确的是,无论是栈还是队列,它们核心的
transfer主逻辑是一致的,差异仅仅在于等待线程的组织方式和匹配时的顺序策略。选择哪一种,取决于你对公平性和吞吐量的权衡。
总而言之,SynchronousQueue的transfer方法展示了一种极致的高并发协作模式。它摒弃了缓冲区的概念,通过一个精巧的Slot和一套经典的同步原语组合,实现了线程间数据传递的零库存、高时效。理解这套机制,对于深入掌握Ja va并发编程的精髓大有裨益。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















