Netty源码(十五):Recycler工具类

1. Recycler工具类的使用

Recycler为了避免我们重复的创建对象,使用对象池将我们使用过的数据保存起来,下一次就可以拿出来使用

public class TestRecycler {
    // 对象池
    private static final Recycler<User> RECYCLER = new Recycler<User>() {
        @Override
        protected User newObject(Handle<User> handle) {
            return new User(handle);
        }
    };

    // 用于测试的对象类
    private static class User{
        private final Recycler.Handle<User> handle;//handle负责对象的回收

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

        public void recycle(){
            handle.recycle(this);
        }
    }

    public static void main(String[] args) {
        User user = RECYCLER.get();

        user.recycle();

        User user1 = RECYCLER.get();

        // 重复利用user对象
        System.out.println(user==user1);
    }
}

执行结果为true表明我们没有new一个新的对象,第二次直接拿了原来的User对象来使用。
在这里插入图片描述
下面是Recycler的数据结构图
在这里插入图片描述

2. Recycler的get()方法

Recycler中维持了一个FastThreadLocal,里面存放的是一个栈,战中存储数据的是DefaultHandle<?>[] elements数组

public final T get() {
        // 判断线程池的容量等于0则直接返回一个Object
        if (maxCapacityPerThread == 0) {
            return newObject((Handle<T>) NOOP_HANDLE);
        }

        // fastThreadLocal中获取一个stack
        Stack<T> stack = threadLocal.get();
        DefaultHandle<T> handle = stack.pop();

        // 试图从"池"中取一个handle,如果没有成功就new一个handle
        if (handle == null) {
            handle = stack.newHandle();
            handle.value = newObject(handle);
        }
        return (T) handle.value;
    }

2.1 跟进pop()方法

	DefaultHandle<T> pop() {
            int size = this.size;// 获取栈的长度
            if (size == 0) {
                if (!scavenge()) {// 当前线程创建的对象跑到其他线程中了(被其他线程回收了),使用scavenge()方法将这些数据拿回来
                    return null;
                }
                size = this.size;
                if (size <= 0) {
                    // double check, avoid races
                    return null;
                }
            }
            size --;
            DefaultHandle ret = elements[size];// 数组尾部的数据拿出来
            elements[size] = null;
            // As we already set the element[size] to null we also need to store the updated size before we do
            // any validation. Otherwise we may see a null value when later try to pop again without a new element
            // added before.
            this.size = size;

            if (ret.lastRecycledId != ret.recycleId) {
                throw new IllegalStateException("recycled multiple times");
            }
            ret.recycleId = 0;
            ret.lastRecycledId = 0;
            return ret;
        }

3. Recycler的recycle()方法

recycle()实现对对象的回收,主要就是将当前对象压入栈中

		// 默认的回收对象的实现方式
        @Override
        public void recycle(Object 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);// 将对象再放入栈中
        }

跟进push()源码

	void push(DefaultHandle<?> item) {
            Thread currentThread = Thread.currentThread();
            if (threadRef.get() == currentThread) {
                // The current Thread is the thread that belongs to the Stack, we can try to push the object now.
                // 同步回收数据
                pushNow(item);
            } else {
                // The current Thread is not the one that belongs to the Stack
                // (or the Thread that belonged to the Stack was collected already), we need to signal that the push
                // happens later.
                pushLater(item, currentThread);
            }
        }

如果是当前线程创建的对象,会直接回收到当前线程的stack中,如果thread1创建的对象,倍thread2回收,就会被放到thread2的weekOrderQueue中
在这里插入图片描述

3.1 同步回收数据

指当前线程创建的对象被自己所回收。直接将其加入stack的DefaultHandle<?>[] elements数组

	private void pushNow(DefaultHandle<?> item) {
            // 第一次回收时recycleId等于0,lastRecycledId等于0
            if ((item.recycleId | item.lastRecycledId) != 0) {
                throw new IllegalStateException("recycled already");
            }
            item.recycleId = item.lastRecycledId = OWN_THREAD_ID;// OWN_THREAD_ID本质是一个getAndIncrement()方法

            int size = this.size;
            // 大于回收上限,扔掉该对象
            if (size >= maxCapacity || dropHandle(item)) {//dropHandle()方法保证回收对象是7/8
                // Hit the maximum capacity or should drop - drop the possibly youngest object.
                return;
            }

            // 超过数组的阈值了,进行扩容为原来2倍
            if (size == elements.length) {
                elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
            }

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

3.2 异步回收数据

指当前线程创建的对象被其他线程所回收,当thread1需要对象时,但是当前线程中的对象为空了,就需要从其他线程中回收对象。
head:指向第一个WeakOrderQueue,也就是图中的thread2
pre:当前需要回收的thread的WeakOrderQueue
cursor:pre的前一个节点

在这里插入图片描述
在之前Recycler中维持的stack的pop方法里面scavenge()就是异步回收数据。
在这里插入图片描述
跟踪Scavenge()方法
在这里插入图片描述
继续跟踪scavengeSome()方法,这里就是从其他线程中回收对象的核心逻辑。

	private boolean scavengeSome() {
            WeakOrderQueue prev;
            WeakOrderQueue cursor = this.cursor;
            if (cursor == null) {// 如果当前需要回收的线程的WeakOrderQueue为null
                prev = null;
                cursor = head;//指针初始化
                if (cursor == null) {
                    return false;// 返回false表示什么都没有回收到
                }
            } else {
                prev = this.prev;
            }

            // 下面这个循环会不断从cursor节点关联的WeakOrderQueue中回收数据
            boolean success = false;
            do {
                if (cursor.transfer(this)) {// 将cursor中的一个对象传输到当前的stack中
                    success = true;
                    break;
                }
                WeakOrderQueue next = cursor.getNext();// 回收失败则指针后移一位,cursor指向下一个WeakOrderQueue
                // cursor线程如果不存在了,做一些清理工作
                if (cursor.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.
                    // 如果线程中有数据,则将数据传输到当前线程的stack中
                    if (cursor.hasFinalData()) {
                        for (;;) {
                            if (cursor.transfer(this)) {
                                success = true;
                            } else {
                                break;
                            }
                        }
                    }

                    // 将cursor节点中链表中移除
                    if (prev != null) {
                        // Ensure we reclaim all space before dropping the WeakOrderQueue to be GC'ed.
                        cursor.reclaimAllSpaceAndUnlink();
                        prev.setNext(next);
                    }
                } else {
                    prev = cursor;
                }

                // cursor指针后移,继续遍历
                cursor = next;

            } while (cursor != null && !success);// 一直遍历到链表尾部,结束循环

            this.prev = prev;
            this.cursor = cursor;
            return success;
        }

回顾一下,WeakOrderQueue的数据结构
在这里插入图片描述
我们继续跟踪一下transfer()方法是如何从其他线程中取数据的

	boolean transfer(Stack<?> dst) {
            Link head = this.head.link;
            // 头节点为空,表示当前的WeakOrederQueue中已经没有数据了
            if (head == null) {
                return false;
            }

            // 如果当前head节点的DefaultHandler数组已经读取到的下标等于link的容量
            // 说明当前link中的数据已经被取走了
            if (head.readIndex == LINK_CAPACITY) {
                if (head.next == null) {
                    return false;
                }
                head = head.next;// head指向下一个link
                this.head.relink(head);
            }

            final int srcStart = head.readIndex;// 当前link可以读取的的下标起点
            int srcEnd = head.get();//当前link目前的数据量个数表示读取的数据终点
            final int srcSize = srcEnd - srcStart;//算出可以回收的对象个数
            if (srcSize == 0) {
                return false;
            }

            final int dstSize = dst.size;//当前的stack的大小
            //如果将当前link中的所有数据全部回收+stack中原来的数据个数就是传输结束后stack的容量
            final int expectedCapacity = dstSize + srcSize;

            // 预期的stack的容量大于satck的大小就对stack扩容
            if (expectedCapacity > dst.elements.length) {
                // actualCapacity表示扩容以后的stack的长度
                final int actualCapacity = dst.increaseCapacity(expectedCapacity);
                // 根据口容以后的容量大小,重新计算link读取数据的结束下标
                srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
            }

            // 开始从当前的link中取数据到stack中
            if (srcStart != srcEnd) {
                final DefaultHandle[] srcElems = head.elements;//link
                final DefaultHandle[] dstElems = dst.elements;//stack
                int newDstSize = dstSize;
                // 遍历当前link完成数据回收到stack
                for (int i = srcStart; i < srcEnd; i++) {
                    DefaultHandle<?> element = srcElems[i];
                    // 获取当前的recycleId表示当前element未被回收,附上我们回收的lastRecycledId
                    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)) {//dropHandle控制回收的频率
                        // Drop the object.
                        continue;
                    }
                    element.stack = dst;
                    dstElems[newDstSize ++] = element;
                }

                // 当前link被回收完了,且,还有下一个节点
                if (srcEnd == LINK_CAPACITY && head.next != null) {
                    // Add capacity back as the Link is GCed.
                    this.head.relink(head.next);// 当前link可以再次接收LINK_CAPACITY个数据了,link的重复利用
                }

                head.readIndex = srcEnd;
                // stack旧的长度和回收数据后的长度比较
                // 相等表示没有回收到数据
                if (dst.size == newDstSize) {
                    return false;
                }
                dst.size = newDstSize;
                return true;
            } else {
                // The destination stack is full already.
                return false;
            }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值