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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值