netty Recycler(三) 多執行緒回收物件時競爭機制的解決

m1f2c3發表於2020-12-16

假設執行緒1建立了大量物件,執行緒2和執行緒3同時回收執行緒1的物件,當這兩個執行緒第一次回收物件時會建立WeakOrderQueue,並將其新增到執行緒1的Stack。多個執行緒操控一個Stack,這就造成了競爭。那麼netty是如何解決競爭?

程式碼

package study.recycler.again;

import io.netty.util.Recycler;

import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 斷點2 主執行緒回收池中會有大量CyclerA
 */
public class CycliMutiThread7 {
    private static final Recycler<CyclerA> CyclerRecyclerA = new Recycler<CyclerA>() {
        @Override
        protected CyclerA newObject(Handle<CyclerA> handle) {
            return new CyclerA(handle);
        }
    };
    static final class CyclerA {
        private String value;
        public void setValue(String value) {
            this.value = value;
        }
        private Recycler.Handle<CyclerA> handle;
        public CyclerA(Recycler.Handle<CyclerA> handle) {
            this.handle = handle;
        }
        public void recycle() {
            handle.recycle(this);
        }
    }
    private static final Recycler<CyclerB> CyclerRecyclerB = new Recycler<CyclerB>() {
        @Override
        protected CyclerB newObject(Handle<CyclerB> handle) {
            return new CyclerB(handle);
        }
    };
    static final class CyclerB {
        private String value;
        public void setValue(String value) {
            this.value = value;
        }
        private Recycler.Handle<CyclerB> handle;
        public CyclerB(Recycler.Handle<CyclerB> handle) {
            this.handle = handle;
        }
        public void recycle() {
            handle.recycle(this);
        }
    }
    private static final Recycler<CyclerC> CyclerRecyclerC = new Recycler<CyclerC>() {
        @Override
        protected CyclerC newObject(Handle<CyclerC> handle) {
            return new CyclerC(handle);
        }
    };
    static final class CyclerC {
        private String value;
        public void setValue(String value) {
            this.value = value;
        }
        private Recycler.Handle<CyclerC> handle;
        public CyclerC(Recycler.Handle<CyclerC> handle) {
            this.handle = handle;
        }
        public void recycle() {
            handle.recycle(this);
        }
    }
    public static void  main(String[] args) throws InterruptedException {
        ConcurrentLinkedQueue<CyclerA> qAThread = new ConcurrentLinkedQueue();
        Thread t = Thread.currentThread();

        for (int i = 0; i < 901; ++i) {
            qAThread.add(CyclerRecyclerA.get());
        }

        Thread t1 = new Thread(() -> {
            Thread temp = t;
            CyclerA cyclerA = qAThread.poll();
            cyclerA.setValue("t1");
            cyclerA.recycle();
        });
        t1.start();
        t1.join();

        Thread t2 = new Thread(() -> {
            Thread temp = t;
            CyclerA cyclerA = qAThread.poll();
            cyclerA.setValue("t2");
            cyclerA.recycle();
        });


        t2.start();
        t2.join();


        System.out.println("over");//斷點2
    }
}

呼叫堆疊

setHead:528, Recycler$Stack (io.netty.util)
newQueue:358, Recycler$WeakOrderQueue (io.netty.util)
newWeakOrderQueue:705, Recycler$Stack (io.netty.util)
pushLater:688, Recycler$Stack (io.netty.util)
push:647, Recycler$Stack (io.netty.util)
recycle:236, Recycler$DefaultHandle (io.netty.util)
recycle:27, CycliMutiThread7$CyclerA (study.recycler.again)
lambda$main$0:80, CycliMutiThread7 (study.recycler.again)
run:-1, 440434003 (study.recycler.again.CycliMutiThread7$$Lambda$1)
run:748, Thread (java.lang)

關鍵程式碼

        // Marked as synchronized to ensure this is serialized.
        synchronized void setHead(WeakOrderQueue queue) {
            queue.setNext(head);
            head = queue;
        }

由上面程式碼可以知道,netty中通過synchronized保證多執行緒安全

此外Recycler中還有Stack中還有兩個函式對理解整個流程(建立新的WeakOrderQueue並將其新增到Stack中)比較重要,下面會按照呼叫順序講解

        private void pushLater(DefaultHandle<?> item, Thread thread) {
            if (maxDelayedQueues == 0) {
                // We don't support recycling across threads and should just drop the item on the floor.
                return;
            }

            // we don't want to have a ref to the queue as the value in our weak map
            // so we null it out; to ensure there are no races with restoring it later
            // we impose a memory ordering here (no-op on x86)
            Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
            WeakOrderQueue queue = delayedRecycled.get(this);
            if (queue == null) {
                if (delayedRecycled.size() >= maxDelayedQueues) {
                    // Add a dummy queue so we know we should drop the object
                    delayedRecycled.put(this, WeakOrderQueue.DUMMY);
                    return;
                }
                // Check if we already reached the maximum number of delayed queues and if we can allocate at all.
                if ((queue = newWeakOrderQueue(thread)) == null) {
                    // drop object
                    return;
                }
                delayedRecycled.put(this, queue);
            } else if (queue == WeakOrderQueue.DUMMY) {
                // drop object
                return;
            }

            queue.add(item);
        }

比如執行緒2回收執行緒1建立的物件,首先會通過物件中註冊的DefaultHandler獲取物件所屬的Stack,然後經過一系列判斷進入到pushLater這個函式中並進行下面操作。
執行緒2第一次回收執行緒1所建立的物件
1、呼叫DELAYED_RECYCLED.get()獲取一個ThreadLocal物件(如果沒有就建立新的)
2、呼叫newWeakOrderQueue(thread)建立並新增WeakOrderQueue

跟蹤newWeakOrderQueue的程式碼,可以看到WeakOrderQueue的這個函式

static WeakOrderQueue newQueue(Stack<?> stack, Thread thread) {
            // We allocated a Link so reserve the space
            if (!Head.reserveSpaceForLink(stack.availableSharedCapacity)) {
                return null;
            }
            final 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);//上文提到過這個函式,用synchronized修飾過

            return queue;
        }

1、new WeakOrderQueue(stack, thread)建立新的WeakOrderQueue。其中入參thread會存放到WeakOrderQueue.referent
2、上文提到過這個函式,WeakOrderQueue放入Stack中。這個函式用synchronized修飾過,以保證多執行緒安全。

相關文章