Recycler 笔记

参考文章:Netty技术细节源码分析-Recycler对象池原理分析Netty之Recycler

定义:

基于线程本地堆栈的轻量级对象池。

四个核心内部类
  • DefaultHandle:对象的包装类,在Recycler中缓存的对象都会包装成DefaultHandle类。
  • Stack:存储本线程回收的对象。对象的获取和回收对应Stack的pop和push,即获取对象时从Stack中pop出1个DefaultHandle,回收对象时将对象包装成DefaultHandle push到Stack中。Stack会与线程绑定,即每个用到Recycler的线程都会拥有1个Stack,在该线程中获取对象都是在该线程的Stack中pop出一个可用对象。
  • WeakOrderQueue:存储其它线程回收到本线程stack的对象,当某个线程从Stack中获取不到对象时会从WeakOrderQueue中获取对象。每个线程的Stack拥有1个WeakOrderQueue链表,链表每个节点对应1个其它线程的WeakOrderQueue,其它线程回收到该Stack的对象就存储在这个WeakOrderQueue里。
  • Link: WeakOrderQueue中包含1个Link链表,回收对象存储在链表某个Link节点里,当Link节点存储的回收对象满了时会新建1个Link放在Link链表尾。

在这里插入图片描述

内部属性

在这里插入图片描述

Stack && WeakIOrderQueue && Link && DefaultHandle 四者关系

在这里插入图片描述

FastThreadLocal

定义:

{@link ThreadLocal} 的一种特殊变体,当从 {@link FastThreadLocalThread} 访问时会产生更高的访问性能。

在内部,{@link FastThreadLocal} 使用数组中的常量索引,而不是使用哈希码和哈希表来查找变量。虽然看起来非常微妙,但它比使用哈希表产生了轻微的性能优势,并且在频繁访问时很有用。

要利用此线程局部变量,您的线程必须是 {@link FastThreadLocalThread} 或其子类型。由于这个原因,默认情况下,由 {@link DefaultThreadFactory} 创建的所有线程都是 {@link FastThreadLocalThread}。

请注意,快速路径仅适用于扩展 {@link FastThreadLocalThread} 的线程,因为它需要一个特殊字段来存储必要的状态。任何其他类型的线程的访问都会退回到常规的 {@link ThreadLocal}。

Stack

我们保留一个每个线程队列的队列,它只附加一次,每次除了堆栈所有者之外的新线程回收时:当我们用完堆栈中的项目时,我们会迭代这个集合以清除可以重用的那些。这允许我们在回收所有项目的同时进行最少的线程同步。

static final class Stack<T> {

    final Recycler<T> parent;
    final Thread thread;
    final AtomicInteger availableSharedCapacity;
    final int maxDelayedQueues;

    private final int maxCapacity;
    private final int ratioMask;
    private DefaultHandle<?>[] elements;
    private int size;
    private int handleRecycleCount = -1; // Start with -1 so the first one will be recycled.
    private WeakOrderQueue cursor, prev;
    private volatile WeakOrderQueue head;
}

注意::
Stack 类中包含 DefaultHandle<?>[] 数组字段,而 DefaultHandle 的结构包含一个Stack<?> 字段,因此,
这个Stack主体是一个堆栈,但是其还维护着一个链表,而链表中的每一个节点都是一个队列, 包含已经被回收的对象。

weakOrderQueue

定义:

一个只对可见性做出适度保证的队列:项目以正确的顺序被看到,但我们不能绝对保证看到任何东西,从而保持队列的维护成本低

private static final class WeakOrderQueue {

        static final WeakOrderQueue DUMMY = new WeakOrderQueue();

        // Let Link extend AtomicInteger for intrinsics. The Link itself will be used as writerIndex.
        @SuppressWarnings("serial")
        private static final class Link extends AtomicInteger {
            private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];

            private int readIndex;
            private Link next;
        }

        // 数据链
        private Link head, tail;
        // 指向同一堆栈的另一个延迟项队列的指针
        private WeakOrderQueue next;
        private final WeakReference<Thread> owner;
        private final int id = ID_GENERATOR.getAndIncrement();
        private final AtomicInteger availableSharedCapacity;

}
Link
定义:

Link 作为一个内部类继承于 AtomicInteger, 主要被用作为 writerIndex(写索引)标志

@SuppressWarnings("serial")
private static final class Link extends AtomicInteger {
    // 存放已被回收的对象
    private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];

    private int readIndex;
    private Link next;
}
DefaultHandle
功能:

回收对象

定义:
public interface Handle<T> {
    void recycle(T object);
}
static final class DefaultHandle<T> implements Handle<T> {
    // 最后一个回收ID
    private int lastRecycledId;
    private int recycleId;

    // 是否已被回收
    boolean hasBeenRecycled;

    private Stack<?> stack;
    // handle 对象
    private Object value;

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

    @Override
    public void recycle(Object object) {
        // 回收本handle的 对象
        if (object != value) {
            throw new IllegalArgumentException("object does not belong to handle");
        }
        stack.push(this);
    }
}

类方法:

WeakOrderQueue
private WeakOrderQueue(Stack<?> stack, Thread thread) {
    head = tail = new Link();
    owner = new WeakReference<Thread>(thread);

    // Its important that we not store the Stack itself in the WeakOrderQueue as the Stack also is used in
    // the WeakHashMap as key. So just store the enclosed AtomicInteger which should allow to have the
    // Stack itself GCed.
    availableSharedCapacity = stack.availableSharedCapacity;
}

Recycler 在 netty 中如何使用

Recycler对象池在netty中最重要的使用,就在于netty的池化ByteBuf的场景下。首先,何为池化?以PooledDirectByteBuf举例,每一个PooledDirectByteBuf在应用线程中使用完毕之后,并不会被释放,而是等待被重新利用,类比线程池每个线程在执行完毕之后不会被立即释放,而是等待下一次执行的时候被重新利用。所谓的对象池也是如此,池化减少了ByteBuf创建和销毁的开销,也是netty高性能表现的基石之一。

private static final Recycler<PooledDirectByteBuf> RECYCLER = new Recycler<PooledDirectByteBuf>() {
    protected PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
        return new PooledDirectByteBuf(handle, 0);
    }
};

// 申请一个ByteBuf对象
static PooledDirectByteBuf newInstance(int maxCapacity) {
    PooledDirectByteBuf buf = (PooledDirectByteBuf)RECYCLER.get();
    buf.reuse(maxCapacity);
    return buf;
}

PooledDirectByteBuf 在其类加载的过程中初始化一个静态成员 RECYCLER通过重写newObject 方法达到使Recycler 可以初始化一个PooledDirectBytebuf 。在接下来的使用中,只需通过静态方法 newInstance() 就可以从RECYCLER 对象池的get() 方法获取一个新的 PooledDirectByteBuf 对象返回,而重写的方法newObject() 中的入参Handle 着提供了 recycler() 方法(如上DefaultHandle.recycler()所示) 给出了对象重放对象池回收的能力, 因此,newInstance() 方法 和recycler() 方法分别提供了对象池的出池和入池能力, 也因此PooledDirectByteBuf 达到了池化的目标。

Recycler 的实现原理分析

Recycler的实现原理很抽象,可以先直接阅读文末的例子再阅读这部分内容。例子如下:

A 线程申请,A 线程回收的场景。

  • 显然,当对象的申请与回收是在一个线程中时,直接把对象放入到 A 线程的对象池中即可,不存在资源的竞争,简单轻松。

A 线程申请,B 线程回收的场景。

  • 首先,当 A 线程通过其对象池申请了一个对象后,在 B 线程调用 recycle()方法回收该对象。显然,该对象是应该回收到 A 线程私有的对象池当中的,不然,该对象池也失去了其意义。
  • 那么 B 线程中,并不会直接将该对象放入到 A 线程的对象池中,如果这样操作在多线程场景下存在资源的竞争,只有增加性能的开销,才能保证并发情况下的线程安全,显然不是 netty 想要看到的。
  • 那么 B 线程会专门申请一个针对 A 线程回收的专属队列,在首次创建的时候会将该队列放入到 A 线程对象池的链表首节点(这里是唯一存在的资源竞争场景,需要加锁),并将被回收的对象放入到该专属队列中,宣告回收结束。
  • 在 A 线程的对象池数组耗尽之后,将会尝试把各个别的线程针对 A 线程的专属队列里的对象重新放入到对象池数组中,以便下次继续使用。
第一种情况,A申请 A 回收
void push(DefaultHandle<?> item) {
    Thread currentThread = Thread.currentThread();
    if (thread == currentThread) {
        // The current Thread is the thread that belongs to the Stack, we can try to push the object now.
        // 如果该stack就是本线程的stack,那么直接把DefaultHandle放到该stack的数组里
        pushNow(item);
    } else {
        // The current Thread is not the one that belongs to the Stack, we need to signal that the push
        // happens later.
        // 如果该stack不是本线程的stack,那么把该DefaultHandle放到该stack的WeakOrderQueue中
        pushLater(item, currentThread);
    }
}

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;
    if (size >= maxCapacity || dropHandle(item)) {
        // Hit the maximum capacity or should drop - drop the possibly youngest object.
        return;
    }
    // 直接把DefaultHandle放到stack的数组里,如果数组满了那么扩展该数组为当前2倍大小
    if (size == elements.length) {
        elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
    }

    elements[size] = item;
    this.size = size + 1;
}
第一个成员是在Recycler被定义的Stack成员对象。
private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
    @Override
    protected Stack<T> initialValue() {
        return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
                            ratioMask, maxDelayedQueuesPerThread);
    }
};

static final class Stack<T> {

    final Recycler<T> parent;
    final Thread thread;
    final AtomicInteger availableSharedCapacity;
    final int maxDelayedQueues;

    private final int maxCapacity;
    private final int ratioMask;
    // 存放当前线程被回收的对象
    private DefaultHandle<?>[] elements;
    private int size;
    private int handleRecycleCount = -1; // Start with -1 so the first one will be recycled.
    private WeakOrderQueue cursor, prev;
    private volatile WeakOrderQueue head;
}

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(Object object) {
        if (object != value) {
            throw new IllegalArgumentException("object does not belong to handle");
        }
        stack.push(this);
    }
}

这个Stack 主体是一个堆栈,内部还维护一个链表elements , 这个链表中的每一个节点都是一个队列。

// 存放当前线程被回收的对象
private DefaultHandle<?>[] elements;
private WeakOrderQueue cursor, prev;

上述的elements数组便是存放当前线程被回收的对象,当前线程要从该线程的Recycler 对象池尝试获取新的对象的时候, 首先从当前Stack 的这个数组中尝试获取已经在先前被创建, 并且在当前线程被回收的对象,因为当对象池的对象在当前线程被调用recycle()的时候,是会直接放到elements数组中等待下一次的利用。
那么问题来了,如果从该线程中被申请的这个对象是在另外一个线程中被调用recycle()方法回收呢?那么该对象就会处于链表中的队列中,当堆栈数组( elements) 中的对象不存在的时候,将会尝试把链表队列中的对象转移到数组中供当前线程获取。那么其他线程是如何把被回收的对象放到这些链表中的队列的呢?接下来就是另一个成员的使命了。

第二个成员是在Recycler中也是通过ThreadLocal所实现的一个线程本地变量,DELAYED_RECYCLED ,是一个Stack和队列的映射Map。
// ThreadLocal<Map<>> threadlocal =  new THreadLocal(),里面放的是map 对象
private static final FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
    new FastThreadLocal<Map<Stack<?>, WeakOrderQueue>>() {
    @Override
    // 返回此线程局部变量的初始值
    protected Map<Stack<?>, WeakOrderQueue> initialValue() {
        return new WeakHashMap<Stack<?>, WeakOrderQueue>();
    }
};

第二个成员DELAYED_RECYCLER 可以通过上文的stack 获取一个WeakOrderQueue 队列。

在前一个成员的解释中提到,当别的线程调用另一个线程的对象池的 recycle()方法进行回收的时候,并不会直接落到持有对象池的线程的 Stack 数组当中,当然原因也很简单,在并发情况下这样的操作显然是线程不安全的,而加锁也会带来性能的开销。因此,netty 在 Recycler 对象池中通过更巧妙的方式解决这一问题。

在前面提到,除了数组,Stack 还持有了一系列队列的组成的链表,这些链表中的每一个节点都是一个队列:

private WeakOrderQueue cursor, prev;
private volatile WeakOrderQueue head;

这些队列又存放着别的线程所回收到当前线程对象池的对象。那么,这些队列就是各个线程针对持有对象池的专属回收队列:

weakOrderQueue

定义:

一个只对可见性做出适度保证的队列:项目以正确的顺序被看到,但我们不能绝对保证看到任何东西,从而保持队列的维护成本低

// 专属回收队列,有顺序
private static final class WeakOrderQueue {

        static final WeakOrderQueue DUMMY = new WeakOrderQueue();

        // Let Link extend AtomicInteger for intrinsics. The Link itself will be used as writerIndex.
        @SuppressWarnings("serial")
        private static final class Link extends AtomicInteger {
            private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];

            private int readIndex;
            private Link next;
        }

        // 数据链
        private Link head, tail;
        // 指向同一堆栈的另一个延迟项队列的指针
        private WeakOrderQueue next;
        private final WeakReference<Thread> owner;
        private final int id = ID_GENERATOR.getAndIncrement();
        private final AtomicInteger availableSharedCapacity;

}

说起来很拗口,看下面的代码

pushLater()

Recycler有1个stack->WeakOrderQueue映射,每个stack会映射到1个WeakOrderQueue,这WeakOrderQueue是该stack关联的其它线程WeakOrderQueue链表的head WeakOrderQueue(这里只关联head Queue, 其他queue 可由next 指针获取)。当其它线程回收对象到该stack时会创建1个WeakOrderQueue中并加到stack的WeakOrderQueue链表中。

private void pushLater(DefaultHandle<?> item, Thread thread) {
    // 我们不想将队列的引用作为弱映射中的值,因此我们将其设为空;
    // 为确保稍后恢复它不会出现竞争,我们在此处强制执行内存排序(x86 上的无操作)
    Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
    // 1 首先获取到stack 对应的WeakOrderQueue 队列
    WeakOrderQueue queue = delayedRecycled.get(this);
    if (queue == null) {
        // 2 如果为空, 判断当前delayedRecycled 的数量是否已经到达最大值
        if (delayedRecycled.size() >= maxDelayedQueues) {
            // 3 、添加一个虚拟队列,以便我们知道我们应该删除对象,标记
            // 如果delayedRecycled满了那么将1个伪造的WeakOrderQueue(DUMMY)放到delayedRecycled中,并丢弃该对象(DefaultHandle)
            delayedRecycled.put(this, WeakOrderQueue.DUMMY);
            return;
        }
        // 检查我们是否已经达到了一个延迟队列的最大数量,以及我们是否可以分配。
        // 指的是 link的数量
        // this 是 stack的 引用
        
        // 4 否则分配一个新的WeakOrderQueue 队列,并设置当前queue 为 队列head
        if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
            // drop object
            return;
        }
        // 5 把新的weakorderQueue 加入到delayedRecycled 中
        delayedRecycled.put(this, queue);
    } else if (queue == WeakOrderQueue.DUMMY) {
        // drop object
        return;
    }
    // 6 这个queue 是哪个queue? headQueue --> queue --> tailQueue? 答案: head
    queue.add(item);
}


/**
 * Allocate a new {@link WeakOrderQueue} or return {@code null} if not possible.
   如果不可能,分配一个新的 {@link WeakOrderQueue} 或返回 {@code null}。
 */
static WeakOrderQueue allocate(Stack<?> stack, Thread thread) {
    // We allocated a Link so reserve the space
    return reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)
        ? WeakOrderQueue.newQueue(stack, thread) : null;
}

private static boolean reserveSpace(AtomicInteger availableSharedCapacity, int space) {
    assert space >= 0;
    for (;;) {
        int available = availableSharedCapacity.get();
        if (available < space) {
            return false;
        }
        if (availableSharedCapacity.compareAndSet(available, available - space)) {
            return true;
        }
    }
}


static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
    WeakOrderQueue queue = new WeakOrderQueue(stack, thread);
    // Done outside of the constructor to ensure WeakOrderQueue.this does not escape the constructor and so
    // may be accessed while its still constructed.
    // 注意这里是重点!!!
    stack.setHead(queue);
    return queue;
}

// 重点来了!!!
// 标记为已同步以确保已序列化。 重新设置头部
synchronized void setHead(WeakOrderQueue queue) {
    queue.setNext(head);
    head = queue;
}

pushLater()方法发生在当一个对象被回收的时候,当 当前线程不是这个对象所申请的时候的线程时,将会通过该对象的 Stack 直接去通过 DELAYED_RECYCLED 映射到一条队列上,如果没有则创建并建立映射,再把该对象放入到该队列中,以上操作结束后该次回收即宣告结束。

WeakOrderQueue的构造函数如下,WeakOrderQueue实现了多线程环境下回收对象的机制,当由其它线程回收对象到stack时会为该stack创建1个WeakOrderQueue,这些由其它线程创建的WeakOrderQueue会在该stack中按链表形式串联起来,每次创建1个WeakOrderQueue会把该WeakOrderQueue作为该stack的head WeakOrderQueue。

private WeakOrderQueue(Stack<?> stack, Thread thread) {
    head = tail = new Link();
    owner = new WeakReference<Thread>(thread);

    // 重要的是我们不要将 Stack 本身存储在 WeakOrderQueue 中,因为 Stack 也在 WeakHashMap 中用作键。
    // 所以只需存储封闭的 AtomicInteger 应该允许堆栈本身 GCed。
    availableSharedCapacity = stack.availableSharedCapacity;
}

如果在操作中,队列是被创建的,会把该队列放置在 Stack 中的链表里的头结点,保证创建该对象的线程在数组空了之后能够通过链表访问到该队列并将该队列中的回收对象重新放到数组中等待被下次重新利用,队列交给 A 线程的链表是唯一的阻塞操作。在这里通过一次阻塞操作,避免后续都不存在资源的竞争问题。

Recycler.get() 方法:

取出该线程对应的stack,从stack中pop出1个DefaultHandle,返回该DefaultHandle的真正对象。

@SuppressWarnings("unchecked")
public final T get() {
    if (maxCapacityPerThread == 0) {
        return newObject((Handle<T>) NOOP_HANDLE);
    }
    // Stack 的来源
    Stack<T> stack = threadLocal.get();
    DefaultHandle<T> handle = stack.pop();
    if (handle == null) {
        handle = stack.newHandle();
        handle.value = newObject(handle);
    }
    return (T) handle.value;
}
stack.pop() 方法
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;
    if (ret.lastRecycledId != ret.recycleId) {
        throw new IllegalStateException("recycled multiple times");
    }
    ret.recycleId = 0;
    ret.lastRecycledId = 0;
    this.size = size;
    return ret;
}

如果该stack的DefaultHandle数组中还有对象可用,那么从该DefaultHandle数组中取出1个可用对象返回,如果该DefaultHandle数组没有可用的对象了,那么执行scavenge()方法,将head WeakOrderQueue中的head Link中的DefaultHandle数组转移到stack的DefaultHandle数组,scavenge方法如下:

scavenge() 方法
boolean scavenge() {
    // continue an existing scavenge, if any
    if (scavengeSome()) {
        return true;
    }

    // reset our scavenge cursor
    prev = null;
    cursor = head;
    return false;
}

具体执行了scavengeSome()方法,清理WeakOrderQueue中部分DefaultHandle到stack,每次尽可能清理head WeakOrderQueue的head Link的全部DefaultHandle,如下:

scavengeSome() 方法:
boolean scavengeSome() {
    WeakOrderQueue prev;
    WeakOrderQueue cursor = this.cursor;
    if (cursor == null) {
        prev = null;
        cursor = head;
        if (cursor == null) {
            return false;
        }
    } else {
        prev = this.prev;
    }

    boolean success = false;
    do {
        // 将当前WeakOrderQueue的head Link的DefaultHandle数组转移到stack的DefaultHandle数组中
        if (cursor.transfer(this)) {
            success = true;
            break;
        }
        WeakOrderQueue next = cursor.next;
        if (cursor.owner.get() == null) {
            // If the thread associated with the queue is gone, unlink it, after
            // performing a volatile read to confirm there is no data left to collect.
            // We never unlink the first queue, as we don't want to synchronize on updating the head.
            if (cursor.hasFinalData()) {
                for (;;) {
                    if (cursor.transfer(this)) {
                        success = true;
                    } else {
                        break;
                    }
                }
            }

            if (prev != null) {
                prev.setNext(next);
            }
        } else {
            prev = cursor;
        }

        cursor = next;

    } while (cursor != null && !success);

    this.prev = prev;
    this.cursor = cursor;
    return success;
}
transfer(Stack<?> dst) 方法

将WeakOrderQueue的head Link中的DefaultHandle数组迁移到stack中:

@SuppressWarnings("rawtypes")
boolean transfer(Stack<?> dst) {
    // head 是WeakOrderQueue 中的字段,指向第一个Link
    Link head = this.head;
    if (head == null) {
        return false;
    }

    /**
     * 如果head Link的readIndex到达了Link的容量LINK_CAPACITY,说明该Link已经被scavengge完了。
     * 这时需要把下一个Link作为新的head Link。
     */
    if (head.readIndex == LINK_CAPACITY) {
        if (head.next == null) {
            return false;
        }
        // 牛逼
        this.head = head = head.next;
    }

    final int srcStart = head.readIndex;
    // head link的回收对象数组的最大位置
    int srcEnd = head.get();
    final int srcSize = srcEnd - srcStart;
    if (srcSize == 0) {
        return false;
    }

    // 回收线程stack 的容量
    final int dstSize = dst.size;
    final int expectedCapacity = dstSize + srcSize;

    // 每次会尽可能scavenge整个head Link,如果head Link的DefaultHandle数组能全部迁移到stack中,stack的DefaultHandle数组预期容量
    // 如果预期容量大于stack的DefaultHandle数组最大长度,说明本次无法将head Link的DefaultHandle数组全部迁移到stack中
    if (expectedCapacity > dst.elements.length) {
        final int actualCapacity = dst.increaseCapacity(expectedCapacity);
        srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
    }

    if (srcStart != srcEnd) {
        final DefaultHandle[] srcElems = head.elements;
        final DefaultHandle[] dstElems = dst.elements;
        int newDstSize = dstSize;
        // 迁移head Link的DefaultHandle数组到stack的DefaultHandle数组
        for (int i = srcStart; i < srcEnd; i++) {
            DefaultHandle element = srcElems[i];
            if (element.recycleId == 0) {
                element.recycleId = element.lastRecycledId;
            } else if (element.recycleId != element.lastRecycledId) {
                throw new IllegalStateException("recycled already");
            }
            srcElems[i] = null;

            if (dst.dropHandle(element)) {
                // Drop the object.
                continue;
            }
            element.stack = dst;
            dstElems[newDstSize ++] = element;
        }
        
        // 当head节点的对象全都转移给stack后,取head下一个节点作为head,下次转移的时候再从新的head转移回收的对象
        if (srcEnd == LINK_CAPACITY && head.next != null) {
            // Add capacity back as the Link is GCed.
            reclaimSpace(LINK_CAPACITY);

            this.head = head.next;
        }

        // 迁移完成后更新原始head Link的readIndex
        head.readIndex = srcEnd;
        if (dst.size == newDstSize) {
            return false;
        }
        dst.size = newDstSize;
        return true;
    } else {
        // The destination stack is full already.
        return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用RecyclerView的简单示例: 1. 首先,在你的项目添加RecyclerView依赖项。在build.gradle文件添加以下依赖项: ``` implementation 'androidx.recyclerview:recyclerview:1.0.0' ``` 2. 在你的布局文件添加RecyclerView: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/my_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 3. 创建一个自定义的ViewHolder类,用于显示RecyclerView的每个项: ``` public class MyViewHolder extends RecyclerView.ViewHolder { public TextView textView; public MyViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.text_view); } } ``` 4. 创建一个RecyclerView.Adapter类,用于管理RecyclerView的数据项: ``` public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> { private List<String> mData; public MyAdapter(List<String> data) { mData = data; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_item_layout, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { String item = mData.get(position); holder.textView.setText(item); } @Override public int getItemCount() { return mData.size(); } } ``` 5. 在你的Activity获取RecyclerView实例,并设置LayoutManager和Adapter: ``` RecyclerView recyclerView = findViewById(R.id.my_recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); MyAdapter adapter = new MyAdapter(myData); recyclerView.setAdapter(adapter); ``` 其,myData是一个包含数据项的List。 6. 运行你的应用程序,你应该能够看到一个包含数据项的RecyclerView。 这就是一个简单的RecyclerView示例。你可以根据自己的需要对其进行自定义和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值