Netty源碼分析——NioEventLoopGroup & NioEventLoop

目錄

 

1、NioEventLoopGroup

1.1 類圖

1.2 初始化

1.創建線程執行器

2.創建EventLoop,並存儲到EventExecutor類型的數組中

3.創建線程選擇器

2、NioEventLoop

2.1 類圖

2.2 selector

2.3 run

1.輪詢io事件

2.處理輪詢到的key

3.執行任務隊列中的task


1、NioEventLoopGroup

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

這裏創建了兩個group, 我們提過這是boss線程組和worker線程組, 其實這兩個線程組就相當於兩個NioEventLoop的集合, 默認每個NioEventLoopGroup創建時, 如果不傳入線程數, 會創建cpu核數*2個NioEventLoop線程, 其中boss線程通過輪詢處理Server的accept事件, 而完成accept事件之後, 就會創建客戶端channel, 通過一定的策略, 分發到worker線程進行處理, 而worker線程, 則主要用於處理客戶端的讀寫事件。

1.1 類圖

NioEventLoopGroup 是一個線程池,繼承了 MultithreadEventLoopGroup

1.2 初始化

從構造函數入手

跟進super, 進入了其父類MultithreadEventExecutorGroup的構造方法中:這裏我們看到, 如果傳入的線程數量參數爲0, 則會給一個默認值, 這個默認值就是兩倍的CPU核數。

public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
    private static final int DEFAULT_EVENT_LOOP_THREADS;
    static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
    }
    
    protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
    }
}

繼續跟代碼之後, 我們就看到了創建NioEventLoop的真正邏輯, 在MultithreadEventExecutorGroup類的構造方法中,創建了 nThreads 個子線程池。

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
        if (executor == null) {
        		//創建一個新的線程執行器(1)
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        // 創建子線程池數組,構造NioEventLoop(2)
        children = new EventExecutor[nThreads];
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                // 創建子線程池
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    // 某一個線程池創建失敗,則關閉之前創建成功的線程池
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
														}
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        //創建線程選擇器(3)
        chooser = chooserFactory.newChooser(children);

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

這邊將代碼主要分爲三個步驟:

1.創建線程執行器

這裏有個new DefaultThreadFactory()創建一個DefaultThreadFactory對象, 這個對象作爲參數傳入ThreadPerTaskExecutor的構造函數, DefaultThreadFactory顧名思義, 是一個線程工廠, 用於創建線程的, 簡單看下這個類的繼承關係:

public class DefaultThreadFactory implements ThreadFactory{//類體}

這裏繼承了jdk底層ThreadFactory類, 用於創建線程

我們繼續跟進ThreadPerTaskExecutor的類中:

public final class ThreadPerTaskExecutor implements Executor {

    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        if (threadFactory == null) {
            throw new NullPointerException("threadFactory");
        }
        this.threadFactory = threadFactory;
    }

    @Override
    public void execute(Runnable command) {
        //起一個線程
        threadFactory.newThread(command).start();
    }
}

這個類非常簡單, 繼承了jdk的Executor類, 從繼承關係中我就能猜想到, 而這個類就是用於開啓線程的線程執行器,再看重寫的 execute(Runnable command) 方法, 傳入一個任務, 然後由threadFactory對象創建一個線程執行該任務。這個execute(Runnable command)方法, 其實就是用開開啓NioEventLoop線程用的。

這樣, 通過 executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()) 這種方式就返回了一個線程執行器Executor, 用於開啓NioEventLoop線程

2.創建EventLoop,並存儲到EventExecutor類型的數組中

這裏通過 children = new EventExecutor[nThreads] 初始化了children屬性, 看下這個屬性的定義:

private final EventExecutor[] children

這裏的children是EventExecutor類型的數組, 其實就是NioEventLoop的集合, 因爲NioEventLoop也是EventExecutor的子類

所以這裏初始化了children數組, 大小爲參數nThreads傳入的線程數量, 默認爲cpu核數的兩倍

後面就是通過for循環來創建NioEventLoop線程,在循環體裏通過 newChild() 創建NioEventLoop, 我們跟newChild(executor, args)方法

protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0], ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

this是NioEventLoopGroup自身, executor就是上一小節講到的線程執行器

跟一下NioEventLoop的構造方法,跟到父類SingleThreadEventExecutor構造方法:

protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor, 
                                    boolean addTaskWakesUp, int maxPendingTasks, 
                                    RejectedExecutionHandler rejectedHandler) {
    super(parent);
    this.addTaskWakesUp = addTaskWakesUp;
    this.maxPendingTasks = Math.max(16, maxPendingTasks);
    
    //初始化了線程執行器
    this.executor = ObjectUtil.checkNotNull(executor, "executor");
    //創建一個任務隊列, 這個任務隊列可以將不屬於NioEventLoop線程的任務放到這個任務隊列中, 通過NioEventLoop線程執行
    taskQueue = newTaskQueue(this.maxPendingTasks);
    rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}

3.創建線程選擇器

chooser = chooserFactory.newChooser(children);

NioEventLoopGroup都綁定一個chooser對象, 作爲線程選擇器, 通過這個線程選擇器, 爲每一個channel分配不同的線程

我們看到newChooser(children)傳入了NioEventLoop數組

我們跟到DefaultEventExecutorChooserFactory類中的newChooser方法:

public EventExecutorChooser newChooser(EventExecutor[] executors) { 
    if (isPowerOfTwo(executors.length)) { 
        return new PowerOfTowEventExecutorChooser(executors);
    } else {
        return new GenericEventExecutorChooser(executors);
    }
}

這裏通過 isPowerOfTwo(executors.length) 判斷NioEventLoop數組的長度是不是2的倍數, 然後根據判斷結果返回兩種選擇器對象, 這裏使用到java設計模式的策略模式

根據這兩個類的名字不難看出, 如果是2的倍數, 使用的是一種高性能的方式選擇線程, 如果不是2的倍數, 則使用一種比較普通的線程選擇方式。

private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;
    PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }
    @Override
    public EventExecutor next() {
        return executors[idx.getAndIncrement() & executors.length - 1];
    }
}

這個類實現了線程選擇器的接口EventExecutorChooser, 構造方法中初始化了NioEventLoop線程數組

重點關注下next()方法, next()方法就是選擇下一個線程的方法, 如果線程數是2的倍數, 這裏通過按位與進行計算, 所以效率極高

private static final class GenericEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;
    GenericEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }
    @Override
    public EventExecutor next() {
        return executors[Math.abs(idx.getAndIncrement() % executors.length)];
    }
}

這個類同樣實現了線程選擇器的接口EventExecutorChooser, 並在造方法中初始化了NioEventLoop線程數組

再看這個類的next()方法, 如果線程數不是2的倍數, 則用絕對值和取模的這種效率一般的方式進行線程選擇

2、NioEventLoop

2.1 類圖

NioEventLoop 是隻有單個線程的線程池,但並不是一個純粹的I/O線程,它除了負責I/O的讀寫之外,還兼顧處理以下兩類任務:

  1. 系統Task:通過調用NioEventLoop的execute(Runnable task)方法實現,Netty有很多系統Task,創建它們的主要原因是:當I/O線程和用戶線程同時操作網絡資源時,爲了防止併發操作導致的鎖競爭,將用戶線程的操作封裝成Task放入消息隊列中,由I/O線程負責執行,這樣就實現了局部無鎖化。

  2. 定時任務:通過調用NioEventLoop的schedule(Runnable command, long delay, TimeUnit unit)方法實現。

2.2 selector

作爲NIO框架的Reactor線程,NioEventLoop需要處理網絡I/O讀寫事件,因此它必須聚合一個多路複用器對象。Selector的初始化非常簡單,直接調用Selector.open()方法就能創建並打開一個新的Selector。Netty對Selector的selectedKeys進行了優化,用戶可以通過io.netty.noKeySetOptimization開關決定是否啓用該優化項。默認不打開selectedKeys優化功能。

Selector selector;
    private SelectedSelectionKeySet selectedKeys;

    private final SelectorProvider provider;

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider) {
        super(parent, executor, false);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        provider = selectorProvider;
        //初始化selector
        selector = openSelector();
    }

    private Selector openSelector() {
        final Selector selector;
        try {
            ////調用jdk底層的api,通過provider.openSelector()創建並打開多路複用器
            selector = provider.openSelector();
        } catch (IOException e) {
            throw new ChannelException("failed to open a new selector", e);
        }
        //如果沒有開啓selectedKeys優化開關,就立即返回。
        if (DISABLE_KEYSET_OPTIMIZATION) {
            return selector;
        }
        //如果開啓了優化開關,需要通過反射的方式從Selector實例中獲取selectedKeys和publicSelectedKeys,
        //將上述兩個成員變量設置爲可寫,通過反射的方式使用Netty構造的selectedKeys包裝類selectedKeySet將原JDK的selectedKeys替換掉。
        try {
            //用這個數據結構替換原生的SelectionKeySet
            SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
						
            //通過反射拿到sun.nio.ch.SelectorImpl這個類的class對象
            Class<?> selectorImplClass = Class.forName("sun.nio.ch.SelectorImpl", false, ClassLoader.getSystemClassLoader());

            //判斷拿到的是不是class對象並且是不是Selector的實現類,如果不是他的實現, 就直接返回原生select
            if (!selectorImplClass.isAssignableFrom(selector.getClass())) {
                return selector;
            }
            
            //通過反射拿到selectedKeys和publicSelectedKeys兩個屬性, 默認這兩個屬性底層都是hashSet方式實現的
            Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
            Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
						
            //設置成可修改的
            selectedKeysField.setAccessible(true);
            publicSelectedKeysField.setAccessible(true);
						
            //將selector的這兩個屬性替換成Netty的selectedKeySet
            selectedKeysField.set(selector, selectedKeySet);
            publicSelectedKeysField.set(selector, selectedKeySet);

            selectedKeys = selectedKeySet;
            logger.trace("Instrumented an optimized java.util.Set into: {}", selector);
        } catch (Throwable t) {
            selectedKeys = null;
            logger.trace("Failed to instrument an optimized java.util.Set into: {}", selector, t);
        }

        return selector;
    }

selector在select()操作時候, 會通過selector.selectedKeys()操作返回一個Set<SelectionKey>, 這個是Set類型。

netty對這個set進行了處理, 使用SelectedSelectionKeySet的數據結構進行替換, 當在select()操作時將key存入一個SelectedSelectionKeySet的數據結構中。

簡單跟一下SelectedSelectionKeySet這個類的構造方法:

SelectedSelectionKeySet() {
        keys = new SelectionKey[1024];
    }

說明這類其實底層是通過數組實現的, 通過操作數組下標會有更高的效率。看下其中重要的方法,add表示添加一個SelectionKey。remove()方法, contains()方法都返回了false, 說明其不支持刪除方法和包含方法。

@Override
    public boolean add(SelectionKey o) {
        if (o == null) {
            return false;
        }

        keys[size++] = o;
        if (size == keys.length) {
            increaseCapacity();
        }

        return true;
    }
    
    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

2.3 run

NioEventLoop 的核心方法是 run 方法

protected void run() {
        for (;;) {
            try {
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;

                    case SelectStrategy.BUSY_WAIT:
                    case SelectStrategy.SELECT:
                    		//輪詢io事件(1)
                        select(wakenUp.getAndSet(false));

                        if (wakenUp.get()) {
                            selector.wakeup();
                        }
                        // fall through
                    default:
                }
                cancelledKeys = 0;
                needsToSelectAgain = false;
                //ioRatio主要是用來控制processSelectedKeys()方法執行時間和任務隊列執行時間的比例, 其中ioRatio默認是50, 所以會走到下一步else
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        runAllTasks();
                    }
                } else {
                		//記錄下開始時間
                    final long ioStartTime = System.nanoTime();
                    try {
                    		//處理輪詢到的key(2)
                        processSelectedKeys();
                    } finally {
                        //計算耗時
                        final long ioTime = System.nanoTime() - ioStartTime;
                        //執行task(3)
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
            // Always handle shutdown even if the loop processing threw an exception.
            try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }

分爲以下3步

1. 輪詢io事件

2. 處理輪詢到的key

3. 執行任務隊列中的task

1.輪詢io事件

首先switch塊中默認會走到SelectStrategy.SELECT中, 執行select(wakenUp.getAndSet(false))方法

參數wakenUp.getAndSet(false)代表當前select操作是未喚醒狀態

進入到select(wakenUp.getAndSet(false))方法中:

private void select(boolean oldWakenUp) throws IOException {
    Selector selector = this.selector;
    try {
        int selectCnt = 0;
        //當前系統的納秒數
        long currentTimeNanos = System.nanoTime();
        //截止時間=當前時間+隊列第一個任務剩餘執行時間,因爲超過之後定時任務不能嚴格按照預定時間執行,
        //delayNanos(currentTimeNanos)代表距定時任務中第一個任務剩餘多長時間,
        //其中定時任務隊列是已經按照執行時間有小到大排列好的隊列, 所以第一個任務則是最近需要執行的任務, selectDeadLineNanos就代表了當前操作不能超過的時間。
        long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
        for (;;) {
            //阻塞時間(毫秒)=(截止時間-當前時間+0.5毫秒),除以1000000表示將計算的時間轉化爲毫秒數
            //最後算出的時間就是selector操作的阻塞時間, 並賦值到局部變量的timeoutMillis中
            long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
            if (timeoutMillis <= 0) {
                if (selectCnt == 0) {
                //時間已經超過了最後截止時間+0.5毫秒,  selectCnt == 0 代表沒有進行select操作, 滿足這兩個條件, 則執行selectNow()之後, 將selectCnt賦值爲1之後跳出循環
                    selector.selectNow();
                    selectCnt = 1;
                }
                break;
            }
            //沒超過截止時間
            if (hasTasks() && wakenUp.compareAndSet(false, true)) {
            //hasTasks()這裏是判斷當前NioEventLoop所綁定的taskQueue是否有任務, 
            //如果有任務, sa則執行selectNow()之後, 將selectCnt賦值爲1之後跳出循環(跳出循環之後去執行任務隊列中的任務)
                selector.selectNow();
                selectCnt = 1;
                break;
            }
            //如果沒有滿足上述條件, 就會執行如下,進行阻塞式輪詢, 並且自增輪詢次數
            int selectedKeys = selector.select(timeoutMillis);
            //輪詢次數
            selectCnt ++;
            
            //如果輪詢到一個事件(selectedKeys != 0), 或者當前select操作需要喚醒(oldWakenUp), 
            //或者在執行select操作時已經被外部線程喚醒(wakenUp.get()), 
            //或者任務隊列已經有任務(hasTask), 或者定時任務隊列中有任務(hasScheduledTasks())
            //如果滿足以上,就跳出循環
            if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
                break;
            }
            //省略
            //記錄下當前時間
            long time = System.nanoTime();
            //當前時間-開始時間>=超時時間(條件成立, 說明已經完整的執行完成了一個阻塞的select()操作, 條件不成立, 有可能發生空輪詢)
            if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
                //代表已經進行了一次阻塞式select操作, 操作次數重置爲1
                selectCnt = 1;
            } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
                //省略日誌代碼
                //如果空輪詢的次數大於一個閾值(512), 解決空輪詢的bug
                rebuildSelector();
                selector = this.selector;
                selector.selectNow();
                selectCnt = 1;
                break;
            }
            currentTimeNanos = time;
        }
        //代碼省略
    } catch (CancelledKeyException e) {
        //省略
    }
}

如果此條件不成立, 說明沒有完整執行select()操作, 可能觸發了一次空輪詢, 根據前一個selectCnt++這步我們知道, 每觸發一次空輪詢selectCnt都會自增

之後會進入第二個判斷 SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD 

其中SELECTOR_AUTO_REBUILD_THRESHOLD默認是512, 這個判斷意思就是空輪詢的次數如果超過512次, 則會認爲是發生了epoll bug, 這樣會通過rebuildSelector()方法重新構建selector, 然後將重新構建的selector賦值到局部變量selector, 執行一次selectNow(), 將selectCnt初始化1, 跳出循環

 

2.處理輪詢到的key

 ioRatio主要是用來控制processSelectedKeys()方法執行時間和任務隊列執行時間的比例, 其中ioRatio默認是50, 所以會走到下一步else,首先通過 final long ioStartTime = System.nanoTime() 記錄下開始時間, 再通過processSelectedKeys()方法處理輪詢到的key。

我們跟到processSelectedKeys()方法中:

private void processSelectedKeys() {
        if (selectedKeys != null) {
        		//如果selectedKeys被netty優化過,走這裏
            processSelectedKeysOptimized();
        } else {
        		//如果selectedKeys沒有被優化過,到這裏
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }
private void processSelectedKeysOptimized() {
				//通過for循環遍歷數組
        for (int i = 0; i < selectedKeys.size; ++i) {
        		//拿到當前的selectionKey
            final SelectionKey k = selectedKeys.keys[i];
            
            //將當前引用設置爲null
            selectedKeys.keys[i] = null;
            
            //獲取channel(NioSeverSocketChannel)
            final Object a = k.attachment();
            
						//判斷channel是不是AbstractNioChannel, 通常情況都是AbstractNioChannel。
            //如果是AbstractNioChannel, 則調用processSelectedKey()方法處理io事件
            if (a instanceof AbstractNioChannel) {
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }

            if (needsToSelectAgain) {
                // null out entries in the array to allow to have it GC'ed once the Channel close
                // See https://github.com/netty/netty/issues/2363
                selectedKeys.reset(i + 1);

                selectAgain();
                i = -1;
            }
        }
    }
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
				//獲取到channel中的unsafe
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        
        //驗證key是否合法的
        if (!k.isValid()) {
        		//如果這個key不是合法的, 說明這個channel可能有問題
            final EventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {
                return;
            }
            if (eventLoop != this || eventLoop == null) {
                return;
            }
            unsafe.close(unsafe.voidPromise());
            return;
        }

        try {
            //如果是合法的, 拿到key的io事件
            int readyOps = k.readyOps();
            //鏈接事件
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                unsafe.finishConnect();
            }
            //寫事件
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                ch.unsafe().forceFlush();
            }
            
						//讀事件和接受鏈接事件
        		//如果當前NioEventLoop是work線程的話, 這裏就是op_read事件
        		//如果是當前NioEventLoop是boss線程的話, 這裏就是op_accept事件
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                //然後會通過channel綁定的unsafe對象執行read()方法用於處理鏈接或者讀寫事件
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

3.執行任務隊列中的task

runAllTasks(ioTime * (100 - ioRatio) / ioRatio);

protected boolean runAllTasks(long timeoutNanos) {
				//從定時任務隊列中聚合任務,也就是將定時任務中找到可以執行的任務添加到taskQueue中
        fetchFromScheduledTaskQueue();
        //從普通taskQ裏面拿一個任務
        Runnable task = pollTask();
        //task爲空, 則直接返回
        if (task == null) {
        		//跑完所有的任務執行收尾的操作
            afterRunningAllTasks();
            return false;
        }

				//如果隊列不爲空
    		//首先算一個截止時間(+50毫秒, 因爲執行任務, 不要超過這個時間)
        final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
        long runTasks = 0;
        long lastExecutionTime;
        //執行每一個任務
        for (;;) {
            safeExecute(task);
						//標記當前跑完的任務
            runTasks ++;
						//當跑完64個任務的時候, 會計算一下當前時間
            if ((runTasks & 0x3F) == 0) {
            		//定時任務初始化到當前的時間
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                //如果超過截止時間則不執行(nanoTime()是耗時的)
                if (lastExecutionTime >= deadline) {
                    break;
                }
            }
            
						//如果沒有超過這個時間, 則繼續從普通任務隊列拿任務
            task = pollTask();
            //直到沒有任務執行
            if (task == null) {
            		//記錄下最後執行時間
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                break;
            }
        }
        
				//收尾工作
        afterRunningAllTasks();
        this.lastExecutionTime = lastExecutionTime;
        return true;
    }
private boolean fetchFromScheduledTaskQueue() {
    long nanoTime = AbstractScheduledEventExecutor.nanoTime();
    //從定時任務隊列中抓取第一個定時任務
    //尋找截止時間爲nanoTime的任務
    Runnable scheduledTask  = pollScheduledTask(nanoTime);
    //如果該定時任務隊列不爲空, 則塞到普通任務隊列裏面
    while (scheduledTask != null) {
        //如果添加到普通任務隊列過程中失敗
        if (!taskQueue.offer(scheduledTask)) {
            //則重新添加到定時任務隊列中
            scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
            return false;
        }
        //繼續從定時任務隊列中拉取任務
        //方法執行完成之後, 所有符合運行條件的定時任務隊列, 都添加到了普通任務隊列中
        scheduledTask = pollScheduledTask(nanoTime);
    }
    return true;
}

參考文獻:

https://www.cnblogs.com/xiangnan6122/p/10203169.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章