Java集合框架终极指南:SynchronousQueue源码解析与高并发设计

SynchronousQueue 是 Java 并发包(java.util.concurrent)中的一个特殊阻塞队列,它 不存储任何元素,而是直接在生产者和消费者之间传递数据。

目录

核心特点

核心方法

适用场景

对比其他队列

核心实现原理

(1) TransferStack(非公平模式,栈结构)

(2) TransferQueue(公平模式,队列结构)

关键数据结构

TransferStack 节点结构

TransferQueue 节点结构 

核心操作机制

(1) 生产者(put 操作)

(2) 消费者(take 操作)

关键代码分析 

(1) TransferStack.transfer()(非公平模式) 

(2) TransferQueue.transfer()(公平模式)

性能特点


核心特点

  • 无容量SynchronousQueue 没有内部存储空间,每个 put() 操作必须等待一个 take() 操作,反之亦然。

  • 直接传递:数据直接从生产者传递给消费者,不经过队列缓存。

  • 公平性可选

    • 非公平模式(默认):采用栈(LIFO)方式匹配生产者和消费者,吞吐量较高。

    • 公平模式:采用队列(FIFO)方式匹配,保证公平性但性能稍低。

  • 适用于高并发任务分发:如线程池(Executors.newCachedThreadPool 默认使用 SynchronousQueue)。

核心方法

方法说明
put(E e)阻塞,直到有消费者取走数据
offer(E e)尝试放入数据,如果没有消费者则立即返回 false
offer(E e, long timeout, TimeUnit unit)带超时的 offer()
take()阻塞,直到有生产者放入数据
poll()尝试取数据,如果没有生产者则立即返回 null
poll(long timeout, TimeUnit unit)带超时的 poll()
isEmpty()始终返回 true(因为没有存储元素)
size()始终返回 0

适用场景

  1. 线程池任务调度
    Executors.newCachedThreadPool() 使用 SynchronousQueue,因为:

    • 新任务到来时,如果有空闲线程,则立即执行;

    • 如果没有空闲线程,则创建新线程(适用于短任务高并发场景)。

  2. 生产者-消费者直接通信
    适用于需要严格同步的生产者-消费者模型,如:

    • 实时任务分发;

    • 线程间直接传递数据,避免缓存。

  3. 替代 CountDownLatch 或 Exchanger
    在某些场景下,可以用 SynchronousQueue 实现线程间的同步交换。

对比其他队列

队列存储元素阻塞行为适用场景
SynchronousQueue❌ 不存储put() 和 take() 必须配对高并发任务分发
ArrayBlockingQueue✅ 有界数组队列满时阻塞 put(),空时阻塞 take()固定大小的缓冲队列
LinkedBlockingQueue✅ 可选有界/无界链表同 ArrayBlockingQueue通用任务队列
PriorityBlockingQueue✅ 无界优先级堆仅 take() 可能阻塞优先级任务调度

核心实现原理

SynchronousQueue 的底层实现基于两种不同的数据结构,取决于构造时选择的公平性:

(1) TransferStack(非公平模式,栈结构)

  • 采用 LIFO(后进先出) 策略,新来的线程可能优先匹配。

  • 使用 CAS(Compare-And-Swap) 进行无锁并发控制。

  • 每个节点 SNode 包含:

    • volatile SNode next:栈中的下一个节点

    • volatile SNode match:匹配的节点

    • volatile Thread waiter:等待的线程

    • Object item:传输的数据

    • int mode:模式(REQUEST/DATA,表示 take 或 put

(2) TransferQueue(公平模式,队列结构)

  • 采用 FIFO(先进先出) 策略,保证公平性。

  • 同样使用 CAS 进行线程安全控制。

  • 每个节点 QNode 包含:

    • volatile QNode next:队列中的下一个节点

    • volatile Object item:传输的数据

    • volatile Thread waiter:等待的线程

    • boolean isData:区分生产者(put)和消费者(take

关键数据结构

TransferStack 节点结构

static final class SNode {
    volatile SNode next;        // 栈中的下一个节点
    volatile SNode match;       // 匹配的节点
    volatile Thread waiter;     // 等待的线程
    Object item;                // 传输的数据
    int mode;                   // 模式(REQUEST/PUT/TAKE)
}

TransferQueue 节点结构 

static final class QNode {
    volatile QNode next;         // 队列中的下一个节点
    volatile Object item;        // 传输的数据
    volatile Thread waiter;      // 等待的线程
    final boolean isData;        // 区分生产者/消费者
}

核心操作机制

SynchronousQueue 的核心操作(put/take)都通过 transfer 方法实现:

(1) 生产者(put 操作)

  1. 如果当前栈/队列为空,或已有其他生产者在等待:

    • 将当前线程封装成 SNode/QNode 并加入栈/队列。

    • 进入阻塞状态(LockSupport.park())。

  2. 如果遇到匹配的消费者(take 操作):

    • 直接交换数据(item)。

    • 唤醒消费者线程(LockSupport.unpark(waiter))。

public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }

(2) 消费者(take 操作)

  1. 如果当前栈/队列为空,或已有其他消费者在等待:

    • 将当前线程封装成 SNode/QNode 并加入栈/队列。

    • 进入阻塞状态(LockSupport.park())。

  2. 如果遇到匹配的生产者(put 操作):

    • 直接获取数据(item)。

    • 唤醒生产者线程(LockSupport.unpark(waiter))。 

public E take() throws InterruptedException {
        E e = transferer.transfer(null, false, 0);
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }

关键代码分析 

(1) TransferStack.transfer()(非公平模式) 

E transfer(E e, boolean timed, long nanos) {
    SNode s = null;
    int mode = (e == null) ? REQUEST : DATA; // REQUEST=take, DATA=put

    for (;;) {
        SNode h = head;
        if (h == null || h.mode == mode) { // 栈空或相同模式(无匹配)
            if (timed && nanos <= 0) return null; // 超时
            s = new SNode(e, h, mode); // 创建新节点
            if (casHead(h, s)) { // CAS 更新栈顶
                SNode m = awaitFulfill(s, timed, nanos); // 阻塞等待匹配
                if (m == s) return null; // 被取消
                return (E) ((mode == REQUEST) ? m.item : s.item); // 返回匹配数据
            }
        } else if (casHead(h, s = h.next)) { // 匹配成功
            LockSupport.unpark(h.waiter); // 唤醒匹配线程
            return (E) ((mode == REQUEST) ? h.item : s.item);
        }
    }
}

(2) TransferQueue.transfer()(公平模式)

E transfer(E e, boolean timed, long nanos) {
    QNode s = null;
    boolean isData = (e != null); // put=true, take=false

    for (;;) {
        QNode t = tail, h = head;
        if (t == null || h == null) continue; // 未初始化

        if (h == t || t.isData == isData) { // 队列空或相同模式
            QNode node = new QNode(e, isData);
            if (!casTail(t, node)) continue; // CAS 入队
            Object x = awaitFulfill(node, timed, nanos); // 阻塞等待
            if (x == node) return null; // 被取消
            return (E) x;
        } else { // 匹配成功
            QNode m = h.next;
            LockSupport.unpark(m.waiter); // 唤醒匹配线程
            return (E) m.item;
        }
    }
}

性能特点

  • 高吞吐量:直接传递,无存储开销。

  • 低延迟:生产者与消费者直接交互,减少中间步骤。

  • 公平模式 vs 非公平模式

    • 非公平模式(栈):吞吐量更高,但可能导致线程饥饿。

    • 公平模式(队列):保证先到先服务,减少饥饿问题,但吞吐量略低。

 更多java集合底层详解,请查看专栏 Java基础之集合专栏 

HashMap详解  HashLink详解 ArrayList详解 CopyOnWriteArrayList详解 HashSet详解 LinkedHashMap详解 LinkedHashSet详解 TreeSet详解 TreeMap详解 ConcurrentHashMap详解 PriorityBlockingQueue 详解 PriorityQueue 详解 ArrayDeque 详解 java集合详解 - LinkedBlockingQueue 详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值