手写一个Netty对象池-第二节

58 阅读16分钟

一、引言

在上一小节中,根据过往线程池的经验,在没有阅读Netty的Recycler对象池源码之前,根据对象池的基本功能,手写了一个简易版的对象池代码。

然而解决多线程并发使用的是ReentrantLock,所有线程的所有操作都要获取同一把锁,在高并发场景下,很难满足业务需求,等待锁的时间有可能比在内存中新建个对象的时间都要长;想要提升性能,还可以使用比如分段锁、有CAS功能的容器(CopyOnWriteArrayList)、每个线程都有属于自己的容器可直接避免这个问题(ThreadLocal)。

本文将根据Netty的Recycler的源码,面对上一小节中的对象池需求,Recycler是怎么实现的,以及为什么可以抗住高并发场景下的IO读写功能。

二、代码

  1. 有获取和回收两个功能

通过 acquire() 方法从池中获取对象,release() 方法将对象归还给池。

1.1 对象存储

对象池场景中,每次获取一个对象,所以可以使用类似栈或队列或者链表结构存储,而最近使用的对象更可能在 CPU 缓存中,所以栈结构更适合对象池,并且要求容器是基于CAS线程安全的,所以Netty自定义了一个高效、线程安全且适配对象复用场景的 “栈式存储结构”Stack,一个Stack对象就是一个对象池容器,其中elements是真正存放元素的数组对象。

static final class Stack<T> {
    // 所属的Recycler
    final MyNettyRecycler<T> parent;
    // 所属线程
    final Thread thread;
    // 栈数组
    private DefaultHandle<?>[] elements;
    // 栈大小
    private final int maxCapacity;
    // 当前栈顶索引
    private int size;
    // 弱引用队列,用于在全局对象池中CAS获取对象
    private volatile WeakOrderQueue cursor, prev, head;
}

1.2 传入某类对象的构造方法

在上一篇文章里,使用的是把构造方法和对象初始化方法作为一个参数传给对象池,但是那种方式只支持某种特定的格式,比如传入一个无参的构造方法,不是很灵活,而Netty则是把对象创建和初始化方法放在使用的地方。

private static final Recycler<MyObject> RECYCLER = new Recycler<MyObject>() {
    @Override
    protected MyObject newObject(Handle<MyObject> handle) {
        // 定义对象创建逻辑,需绑定Handle以支持后续回收
        return new MyObject(handle);
    }
};

// 待复用的对象类
static class MyObject {
    private final Recycler.Handle<MyObject> handle;

    public MyObject(Recycler.Handle<MyObject> handle) {
        this.handle = handle;
    }

    // 回收对象到池中的方法
    public void recycle() {
        // 在这里需要用户自己实现对象初始化,比如把id这个变量初始化成-1
        handle.recycle(this);
    }
}

Recycler是一个抽象类,里面有newObject需要使用者自定义,同时还定义了Handle类和DefaultHandle类,作为对象如何"清理状态" 和 "如何被回收" 的标准逻辑。

1.3 获取

Netty选择无锁化的方式避免线程之间的竞争,给每个线程都分配了一个对象池,acquire() 方法大致流程是:(1)优先从自己线程的对象池中获取,无任何竞争(2)若私有池为空,再从全局共享池中获取一次,这里是通过 CAS 无锁操作(3)若全局池也为空,才新建对象。

public final T get() {
    // 从fastThreadLocal中获取线程私有的对象池stack
    Stack<T> stack = (Stack<T>) fastThreadLocal.get();
    // 从对象池获取对象
    DefaultHandle<T> handle = stack.pop();
    // 对象池为空,需要新建对象
    if (handle == null) {
        // 创建一个DefaultHandle对象
        handle = stack.newHandle();
        // 这里的newObject方法即上面定义RECYCLER的方法
        handle.value = newObject(handle);
    }
    return (T) handle.value;
}

DefaultHandle<T> pop() {
    // 当前栈顶索引
    int size = this.size;
    if (size == 0) {
        // 去全局对象池拿对象,代码比较复杂,不展开描述了
        if (!scavenge()) {
            return null;
        }
        size = this.size;
    }
    // 从栈里弹出对象
    size--;
    DefaultHandle ret = elements[size];
    elements[size] = null;
    this.size = size;
    return ret;
}

此外还设计了FastThreadLocal替代ThreadLocal,使得线程内的存取更加高效。FastThreadLocal用一个 Object 数组存储本地变量,直接通过 index 访问数组元素,而ThreadLocal还需要哈希计算,并且哈希表的扩容、链表处理也会产生额外开销。

public abstract class MyNettyRecycler<T> {
    private final FastThreadLocal<Stack<T>> fastThreadLocal = new FastThreadLocal<>() {
        @Override
        protected Stack<T> initialValue() {
            // 初始化对象池
            return new Stack<T>();
        }
    };
}

1.4 回收

对象的回收方法执行是从对象类开始,就比如1.2小节中的MyObject类,需要先把自身的一些属性设置成出初始化状态,再执行DefaultHandle的recycle方法,执行标准的对象池回收逻辑。

static final class DefaultHandle<T> implements Handle<T> {
    // ...省略部分代码
    @Override
    public void recycle(T object) {
        Stack<?> stack = this.stack;
        stack.push(this);
    }
}
    
void push(DefaultHandle<?> item) {
    Thread currentThread = Thread.currentThread();
    if (thread == currentThread) {
        // 放在当前线程的Stack里面
        pushNow(item);
    } else {
        pushLater(item, currentThread);
    }
}

// 核心逻辑很简单,O(1)的时间复杂度
private void pushNow(DefaultHandle<?> item) {
    if ((item.recycleId | item.lastRecycledId) != 0) {
        throw new IllegalStateException("recycled already");
    }
    item.recycleId = item.lastRecycledId = OWN_THREAD_ID;

    int size = this.size;
    // 按设定的频率决定是否放入,比如执行8次pushNow方法,前7次都直接抛弃,第8次才放回elements
    if (size >= maxCapacity || dropHandle(item)) {
        return;
    }
    if (size == elements.length) {
        elements = expandArray();
    }

    elements[size] = item;
    this.size = size + 1;
}

private void pushLater(DefaultHandle<?> item, Thread thread) {
    // 获取当前线程的延迟回收队列映射表
    // DELAYED_RECYCLED是一个ThreadLocal,存储每个线程对应的回收队列集合
    Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();

    // this表示线程1创建的那个Stack对象
    WeakOrderQueue queue = delayedRecycled.get(this);

    // 如果队列不存在,则需要创建新线程1对应的队列
    if (queue == null) {
        // 如果超过最大允许数量,则放入一个空队列标记,避免重复创建
        if (delayedRecycled.size() >= maxDelayedQueuesPerThread) {
            delayedRecycled.put(this, WeakOrderQueue.DUMMY);
            return;
        }

        // 为当前栈和线程分配一个新的弱引用队列
        if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
            return;
        }

        // 将新创建的队列存入映射表中
        delayedRecycled.put(this, queue);
    }
    // 如果队列是一个空队列标记,则直接返回,不进行处理
    else if (queue == WeakOrderQueue.DUMMY) {
        return;
    }

    // 将元素添加到延迟队列的Link对象中
    queue.add(item);
}

假如对象A在线程1被生成,在线程2里面执行对象A的recycle方法,会走到pushLater方法,放到线程2的WeakOrderQueue队列的自定义类Link里面。"queue = delayedRecycled.get(this)"会去拿线程1对应的WeakOrderQueue。拿到后若"queue == null",那么就说明线程2从来没有回收过线程1的对象。"queue = newWeakOrderQueue(thread)"会创建一个WeakOrderQueue,也就是去为线程2去分配一个线程1的Stack对应的WeakOrderQueue,然后通过delayedRecycled.put(this, queue)绑定线程1。(参考自cloud.tencent.com/developer/a…

这里的关键设计是 WeakOrderQueue用弱引用持有,避免内存泄漏;线程1后续操作自己的私有栈时(如 pop() 取对象),会主动从 WeakOrderQueue 中 “窃取” Handle 到自己的栈,实现跨线程回收对象的复用。可参考:www.modb.pro/db/177458和h…

  1. 并发控制

在上一小节文章里面,在获取、回收、定时清理不使用对象时,都要先获取到锁,而 Recycler 的设计完全规避了全局锁竞争引发的性能问题:

  (1)用线程本地缓存FastThreadLocal隔离大多数操作,让高频的获取/回收无需跨线程交互

  (2)在跨线程获取/回收时,用 WeakOrderQueue 和 CAS 实现无锁中转,避免锁竞争;

  (3)用弱引用自动清理线程销毁后的冗余队列,无需额外维护成本。

这种设计让 Recycler 在高频对象复用场景中,既能支持多线程并发,又能保持极低的性能开销。

  1. 设置最大上限

在线程池里面,会有corePoolSize和maximumPoolSize限制核心线程数和存活线程的最大数量,避免线程过多导致 CPU 上下文切换频繁以及内存资源的浪费;

在 Netty 的 Recycler 中,maxCapacityPerThread 和 availableSharedCapacity是控制线程本地缓存的对象数量上限的两个重要参数,分别表示"单个线程Stack中的对象数上限"和"跨线程回收时的共享对象数上限"

maxCapacityPerThread主要防止单个线程占用过多内存

// 在Stack类中,最大容量,即maxCapacityPerThread
private final int maxCapacity;
// 当前大小
private int size;              

private void pushNow(DefaultHandle<?> item) {
    int size = this.size;
    // 超过上限,直接丢弃
    if (size >= maxCapacity || dropHandle(item)) {
        return;  
    }
    // ...
}

availableSharedCapacity用于控制跨线程对象回收的总容量,防止WeakOrderQueue占用过多内存。

当线程 A 向线程 B 的 WeakOrderQueue 中首次添加对象时,需要为线程 B 创建一个专属的 WeakOrderQueue,会先检查 “当前全局跨线程回收对象总数” 是否超过 availableSharedCapacity,若未超过,则允许添加,否则直接丢弃,避免共享缓存过度膨胀。

static boolean reserveSpaceForLink(AtomicInteger availableSharedCapacity) {
    int available;
    do {
        available = availableSharedCapacity.get();
        // 容量不足,返回false
        if (available < Recycler.LINK_CAPACITY) {
            return false;
        }
    } while(!availableSharedCapacity.compareAndSet(available, available - Recycler.LINK_CAPACITY));

    return true;
}
  1. 对象生命周期管理

Recycler的定位是一款轻量级、高性能的对象池化工具,所以并没有复杂主动清理机制,比如给每个在池内未被使用的对象都设置一个过期时间,定时去清理过期对象,那Recycler是怎么保证不会占满内存呢?

(1)Recycler线程本地Stack中的elements数组用于存储"自己创建并回收"的对象,是强引用的关系,若 Recycler 用于线程池中的临时线程,比如业务处理线程,当线程执行完任务后销毁,其本地 Stack 中长时间未使用的对象会被 GC 自动清理,不会造成内存泄漏,依赖 JVM 垃圾回收和线程生命周期关联来间接处理,这种设计可以避免主动清理带来的额外开销。

(2)Stack中的WeakOrderQueue使用弱引用绑定线程,用于存储"自己创建并其他线程回收"的对象,弱引用关联的对象(此处是线程),若没有其他强引用(即线程被销毁了),GC 会主动回收该对象,通过弱引用绑定归属线程,WeakOrderQueue 不会阻碍线程对象的回收,GC也会清理 WeakOrderQueue 自身的内存(包括队列中的 Link 链表等),从根源上避免了线程销毁后的内存泄漏。

(3)Recycler通过maxCapacityPerThread和availableSharedCapacity两个核心参数,限制回收对象的总数量,避免无限制缓存导致内存溢出。还有interval和delayedQueueInterval两个参数,分别控制pushNow时放到elements的频率和pushLater时将对象存入WeakOrderQueue的频率。这些参数可以防止回收对象过多导致内存膨胀。

  1. 内存管理优化

当进行数据传输时,ByteBuf作为网络数据传输的载体,用于替代Java NIO的ByteBuffer,而ByteBuf是一个抽象类,它的实现类可以按三个维度来进行分类:一个是堆内和堆外,一个是Unsafe和非Unsafe,一个是Pooled和非Pooled(引用自:cloud.tencent.com/developer/a…

其中PooledDirectByteBuf是Netty中基于内存池(Pooled)实现的堆外内存(Direct)缓冲区,使用了Recycler对象池,当PooledDirectByteBuf被创建时,会绑定到已分配的堆外内存块;作为缓冲区,它并不需要把字节数组再转成堆内内存,直接操作堆外的字节数组即可,避免了Java堆内存与堆外内存之间的复制开销,适合高频 IO 场景。

final class PooledDirectByteBuf extends PooledByteBuf<ByteBuffer> {
    private static final ObjectPool<PooledDirectByteBuf> RECYCLER = ObjectPool.newPool(new ObjectPool.ObjectCreator<PooledDirectByteBuf>() {
        public PooledDirectByteBuf newObject(ObjectPool.Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    });
}

三、总结

Netty 的Recycler是一款为高频对象复用场景设计的轻量级对象池,以下是对Recycler设计思想的总结:

(1)Recycler通过FastThreadLocal为每个线程分配私有Stack,让 90% 以上的获取和回收操作在单线程内完成,无需锁竞争。

(2)WeakOrderQueue通过弱引用关联线程,引用的线程销毁后自动被 GC 回收,无需手动维护跨线程队列的生命周期,减少了额外开销。

(3)跨线程回收时,WeakOrderQueue的Link节点采用分段存储,通过AtomicInteger的lazySet和compareAndSet实现无锁写入,避免了全局锁的性能瓶颈,可复用于高并发队列设计。

四、详细代码

截取部分4.1.52.Final版本的Recycler关键源码,加上一些注释方便理解,作为参考


import io.netty.util.concurrent.FastThreadLocal;

import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 这是Netty Recycler的完整实现,包含核心组件
*/
public abstract class MyNettyRecycler<T> {

    private static final int DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD = 4 * 1024;
    private static final int DEFAULT_MAX_SHARED_CAPACITY_FACTOR = 2;
    private static final int DEFAULT_LINK_CAPACITY = 16;
    private static final int DEFAULT_MAX_DELAYED_QUEUES_PER_THREAD = 2;

    // 每个线程本地缓存可容纳的最大对象数量
    private final int maxCapacityPerThread;
    // 全局共享缓存的最大容量
    private final int maxSharedCapacityFactor;
    // 单个链表中可存储的对象数量
    private final int linkCapacity;
    // 每个线程可关联的延迟队列(DelayedQueue)的最大数量
    private final int maxDelayedQueuesPerThread;

    // 线程本地存储
    private final FastThreadLocal<Stack<T>> fastThreadLocal = new FastThreadLocal<>() {
        @Override
        protected Stack<T> initialValue() {
            return new Stack<T>(MyNettyRecycler.this, Thread.currentThread(), maxCapacityPerThread,
                    maxSharedCapacityFactor, linkCapacity, maxDelayedQueuesPerThread);
        }
    };

    protected MyNettyRecycler(int maxCapacityPerThread) {
        this(maxCapacityPerThread, DEFAULT_MAX_SHARED_CAPACITY_FACTOR, DEFAULT_LINK_CAPACITY, DEFAULT_MAX_DELAYED_QUEUES_PER_THREAD);
    }

    protected MyNettyRecycler(int maxCapacityPerThread, int maxSharedCapacityFactor,
                           int linkCapacity, int maxDelayedQueuesPerThread) {
        this.maxCapacityPerThread = maxCapacityPerThread;
        this.maxSharedCapacityFactor = maxSharedCapacityFactor;
        this.linkCapacity = linkCapacity;
        this.maxDelayedQueuesPerThread = maxDelayedQueuesPerThread;
    }

    /**
* 获取对象
*/
public final T get() {
        if (maxCapacityPerThread == 0) {
            return newObject(NOOP_HANDLE);
        }

        // 从fastThreadLocal中获取线程私有的对象池stack
        Stack<T> stack = (Stack<T>) fastThreadLocal.get();
        // 从对象池获取对象
        DefaultHandle<T> handle = stack.pop();
        // 对象池为空,需要新建对象
        if (handle == null) {
            handle = stack.newHandle();
            handle.value = newObject(handle);
        }

        return (T) handle.value;
    }

    /**
* 创建新对象 - 由子类实现
*/
protected abstract T newObject(Handle<T> handle);

    /**
* 对象句柄接口
*/
public interface Handle<T> {
        void recycle(T object);
    }

    /**
* 默认句柄实现
*/
static final class DefaultHandle<T> implements Handle<T> {
        private int lastRecycledId;
        private int recycleId;

        boolean hasBeenRecycled;

        private Stack<?> stack;
        private Object value;

        DefaultHandle(Stack<?> stack) {
            this.stack = stack;
        }

        @Override
        public void recycle(T object) {
            if (object != value) {
                throw new IllegalArgumentException("object does not belong to handle");
            }

            Stack<?> stack = this.stack;
            if (lastRecycledId != recycleId || stack == null) {
                throw new IllegalStateException("recycled already");
            }

            // 执行回收操作
            stack.push(this);
        }
    }

    /**
* 线程本地栈
*/
static final class Stack<T> {

        // 所属的Recycler
        final MyNettyRecycler<T> parent;
        // 所属线程
        final Thread thread;
        // 栈数组
        private DefaultHandle<?>[] elements;
        // 栈大小
        private final int maxCapacity;
        // 当前栈顶索引
        private int size;
        // 弱引用队列
        private volatile WeakOrderQueue cursor, prev;
        // 共享容量
        private volatile int sharedCapacity;
        // 可用共享容量
        private int availableSharedCapacity;

        Stack(MyNettyRecycler<T> parent, Thread thread, int maxCapacity,
              int maxSharedCapacityFactor, int linkCapacity, int maxDelayedQueuesPerThread) {
            this.parent = parent;
            this.thread = thread;
            this.maxCapacity = maxCapacity;
            elements = new DefaultHandle[Math.min(maxCapacity, DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD)];
            this.availableSharedCapacity = maxSharedCapacityFactor * maxCapacity;
            this.maxDelayedQueuesPerThread = maxDelayedQueuesPerThread;
        }

        /**
* 创建新句柄
*/
DefaultHandle<T> newHandle() {
            return new DefaultHandle<T>(this);
        }

        /**
* 弹出对象
*/
DefaultHandle<T> pop() {
            int size = this.size;
            if (size == 0) {
                // 从弱引用队列中转移可回收对象到当前线程的缓存
                if (!scavenge()) {
                    return null;
                }
                size = this.size;
            }
            size--;
            DefaultHandle ret = elements[size];
            elements[size] = null;
            this.size = size;
            return ret;
        }

        /**
* 推入对象
*/
void push(DefaultHandle<?> item) {
            // 获取当前执行线程的引用
            Thread currentThread = Thread.currentThread();

            // 判断当前线程是否为容器所属线程
            if (thread == currentThread) {
                // 同一线程则立即执行推入操作
                pushNow(item);
            } else {
                // 不同线程则延迟执行推入操作,并传入当前线程信息
                pushLater(item, currentThread);
            }
        }

        /**
* 立即推入(当前线程)
* 直接在当前线程中执行元素推入操作,不涉及线程切换
*/
private void pushNow(DefaultHandle<?> item) {
            // 检查元素是否已被回收(recycleId和lastRecycledId任一不为0表示已回收)
            if ((item.recycleId | item.lastRecycledId) != 0) {
                // 如果已回收则抛出异常,避免重复回收或使用已回收资源
                throw new IllegalStateException("recycled already");
            }

            // 标记元素的回收ID为当前线程ID,标识该元素归属于当前线程
            item.recycleId = item.lastRecycledId = OWN_THREAD_ID;

            // 获取当前容器中元素的数量
            int size = this.size;

            // 检查是否超过最大容量或需要丢弃该元素
            // 如果超过容量或丢弃条件成立,则不执行推入操作
            if (size >= maxCapacity || dropHandle(item)) {
                return;
            }

            // 如果当前数组已满,进行扩容操作
            if (size == elements.length) {
                elements = expandArray();
            }

            // 将元素放入数组的末尾
            elements[size] = item;
            // 更新容器中元素的数量
            this.size = size + 1;
        }

         /**
* 放入延迟队列
*/
        private void pushLater(DefaultHandle<?> item, Thread thread) {
            // 获取当前线程的延迟回收队列映射表
            // DELAYED_RECYCLED是一个ThreadLocal,存储每个线程对应的回收队列集合
            Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
        
            // this表示线程1创建的那个Stack对象
            WeakOrderQueue queue = delayedRecycled.get(this);
        
            // 如果队列不存在,则需要创建新线程1对应的队列
            if (queue == null) {
                // 如果超过最大允许数量,则放入一个空队列标记,避免重复创建
                if (delayedRecycled.size() >= maxDelayedQueuesPerThread) {
                    delayedRecycled.put(this, WeakOrderQueue.DUMMY);
                    return;
                }
        
                // 为当前栈和线程分配一个新的弱引用队列
                if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
                    return;
                }
        
                // 将新创建的队列存入映射表中
                delayedRecycled.put(this, queue);
            }
            // 如果队列是一个空队列标记,则直接返回,不进行处理
            else if (queue == WeakOrderQueue.DUMMY) {
                return;
            }
        
            // 将元素添加到延迟队列的Link对象中
            queue.add(item);
        }

        /**
* 清理弱引用队列
*/
boolean scavenge() {
            // 转移可以对象以及清理已过期的WeakOrderQueue
//            if (scavengeSome()) {
//                return true;
//            }

            // 重置cursor
            // ......

            return false;
        }

        /**
* 扩展数组
*/
private DefaultHandle<?>[] expandArray() {
            // 保存原数组引用
            DefaultHandle<?>[] oldArray = elements;

            // 计算新容量:原容量左移1位(即原容量 * 2)
            int newSize = oldArray.length << 1;

            // 确保新容量不超过最大允许容量
            if (newSize > maxCapacity) {
                newSize = maxCapacity;
            }

            // 创建新容量的数组
            DefaultHandle<?>[] newArray = new DefaultHandle[newSize];

            // 将原数组中的元素复制到新数组中
            System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);

            // 返回扩容后的新数组
            return newArray;
        }

        /**
* 是否丢弃句柄
* 根据句柄的回收状态和回收计数决定是否丢弃该元素
*  @param  handle 要判断的元素句柄
*  @return  true表示需要丢弃,false表示保留
*/
boolean dropHandle(DefaultHandle<?> handle) {
            // 检查元素是否尚未被回收
            if (!handle.hasBeenRecycled) {
                // 递增最后回收ID,并与比例掩码进行位运算
                // 此处通过位运算实现基于比例的随机丢弃策略
                if ((++handle.lastRecycledId & ratioMask) != 0) {
                    return true; // 满足丢弃条件,返回true
                }
                // 标记元素为已回收状态
                handle.hasBeenRecycled = true;
            }
            // 已回收的元素不丢弃,返回false
            return false;
        }

        // 线程ID
        private static final int OWN_THREAD_ID = (int) (Thread.currentThread().getId());
        // 比例掩码
        private static final int ratioMask = 7;
        // 最大延迟队列数
        private final int maxDelayedQueuesPerThread;
    }

    /**
* 弱引用队列
*/
static final class WeakOrderQueue extends WeakReference<Thread> {

        static final WeakOrderQueue DUMMY = new WeakOrderQueue();

        private final Head head;
        private Link tail;
        private final int maxCapacity;
        private final Stack<?> stack;
        private WeakOrderQueue next;

        private WeakOrderQueue() {
            super(null);
            head = new Head(0);
            maxCapacity = 0;
            stack = null;
        }

        private WeakOrderQueue(Stack<?> stack, Thread thread) {
            super(thread);
            head = new Head(stack.availableSharedCapacity);
            this.stack = stack;
            this.maxCapacity = stack.maxCapacity;
            this.tail = new Link();
        }

        /**
* 分配WeakOrderQueue
*/
static WeakOrderQueue allocate(Stack<?> stack, Thread thread) {
            return new WeakOrderQueue(stack, thread);
        }

        /**
* 添加对象
*/
void add(DefaultHandle<?> handle) {
            // 将当前队列的ID赋值为元素的最后回收ID,建立元素与队列的关联
            handle.lastRecycledId = id;

            // 获取当前队列的尾节点
            Link tail = this.tail;
            int writeIndex;

            // 获取尾节点当前的写入索引,判断是否已达到节点容量上限
            if ((writeIndex = tail.get()) == LINK_CAPACITY) {
                // 尝试为新节点预留空间,若预留失败则直接返回(通常因容量不足)
                if (!reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)) {
                    return;
                }
                // 创建新的节点作为尾节点,并更新链表结构
                this.tail = tail = tail.next = new Link();
                // 获取新尾节点的初始写入索引(默认为0)
                writeIndex = tail.get();
            }

            // 将元素存入尾节点的对应索引位置
            tail.elements[writeIndex] = handle;
            // 清除元素与原所属栈的关联,避免线程间引用混乱
            handle.stack = null;
            // 原子更新尾节点的写入索引(+1),确保线程安全
            tail.lazySet(writeIndex + 1);
        }

        /**
* 保留空间
*/
private boolean reserveSpace(int availableSharedCapacity, int space) {
            return reserveSpace(head, availableSharedCapacity, space);
        }

        private boolean reserveSpace(Head head, int availableSharedCapacity, int space) {
            return head.reserveSpace(availableSharedCapacity, space);
        }

        // 链接容量
        private static final int LINK_CAPACITY = 16;
        // 线程ID
        private static final int id = (int) (Thread.currentThread().getId());
    }

    /**
* 队列头
*/
static final class Head {
        private final AtomicInteger availableSharedCapacity;

        Link link;

        Head(int availableSharedCapacity) {
            this.availableSharedCapacity = new AtomicInteger(availableSharedCapacity);
        }

        /**
* 保留空间
*/
boolean reserveSpace(int availableSharedCapacity, int space) {
            return reserveSpace(this.availableSharedCapacity, availableSharedCapacity, space);
        }

        private boolean reserveSpace(AtomicInteger availableSharedCapacity, int availableSharedCapacity0, int space) {
            int currentAvailable = availableSharedCapacity.get();
            if (currentAvailable < space) {
                return false;
            }
            return availableSharedCapacity.compareAndSet(currentAvailable, currentAvailable - space);
        }
    }

    /**
* 链接节点
*/
static final class Link extends AtomicInteger {
        private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];
        private int readIndex;
        Link next;

        private static final int LINK_CAPACITY = 16;
    }

    // 线程本地延迟回收
    private static final ThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
            new ThreadLocal<Map<Stack<?>, WeakOrderQueue>>() {
                @Override
                protected Map<Stack<?>, WeakOrderQueue> initialValue() {
                    return new WeakHashMap<Stack<?>, WeakOrderQueue>();
                }
            };

    // 空操作句柄,执行recycle方法时,什么都不操作
    private final Handle<T> NOOP_HANDLE = object -> {
    };
}