netty5笔记-线程模型3-EventLoop

        NioEventLoop相对NioEventLoopGroup来说就复杂很多了,需要一定的耐心来看这篇文章。

        首先从NioEventLoop的启动讲起,对于线程池来说,启动一般都是从第一个任务的添加开始的。经过跟踪,找到execute()方法在SingleThreadEventExecutor类中:

    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }

        // inEventLoop表示启动线程与当前线程相同,相同表示已经启动,不同则有两种可能:未启动或者线程不同
        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            // 运行中则直接添加任务到队列中
            addTask(task);
        } else {
            // 尝试启动任务
            startExecution();
            // 将任务加到任务队列taskQueue中
            addTask(task);
            // 发现已经关闭则移除任务并拒绝
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }

        if (!addTaskWakesUp && wakesUpForTask(task)) {
            // 唤醒执行线程
            wakeup(inEventLoop);
        }
    }
    private void startExecution() {
        // 未启动的状态下才进行启动
        if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                // 增加一个定时任务,该任务将定时任务队列中的已取消任务从队列中移除,该任务每间隔1秒执行1次
                schedule(new ScheduledFutureTask<Void>(
                        this, Executors.<Void>callable(new PurgeTask(), null),
                        ScheduledFutureTask.deadlineNanos(SCHEDULE_PURGE_INTERVAL), -SCHEDULE_PURGE_INTERVAL));
                // 开始执行
                scheduleExecution();
            }
        }
    }
    
    // 如果已经关闭了,则不能再加任务,否则加入到任务队列中
    protected void addTask(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        if (isShutdown()) {
            reject();
        }
        taskQueue.add(task);
    }

      简单的部分就不用讲了,我们来看看两个可能会让人疑惑的点:

      1、scheduleExecution()

      这个方法是将asRunnable提交到executor,由于线程池的线程数与EventExecutor的个数相同,所以可以保证每次asRunnable都能及时处理, asRunnable逻辑比较简单,执行所在类中的run方法,这个run方法是个抽象方法,它的实现有几个要求要满足:

      a、run方法中只执行一定量的任务。如果执行太多,或者一直执行不跳出,那么后期netty中期望引入的fork/jion框架stealing机制就会失效或者大打折扣;

      b、run方法执行完一定量任务后,本次任务完成,此时需要调用scheduleExecution(),否则该EventExecutor后面的任务将无法进行;

      c、基于b中子类必须调用scheduleExecution()的要求,任务的执行必须使用try catch方式。如果不这样的话,发生任何异常都会导致EventExecutor关闭,里面的所有任务都将被清理。

       另一个需要注意的点是scheduleExecution方法在执行asRunnable前将thread置为null了,该thread表示EventLoop所在线程,由于executor.execute的执行并不能保证是哪个Thread来执行,因此先把thread置为null,等进行asRunnable的run方法后再次设置thread为Thread.currentThread。

    protected final void scheduleExecution() {
        updateThread(null);
        executor.execute(asRunnable);
    }

    private final Runnable asRunnable = new Runnable() {
        @Override
        public void run() {
            updateThread(Thread.currentThread());
            // lastExecutionTime must be set on the first run
            // in order for shutdown to work correctly for the
            // rare case that the eventloop did not execute
            // a single task during its lifetime.
            if (firstRun) {
                firstRun = false;
                updateLastExecutionTime();
            }
            try {
                SingleThreadEventExecutor.this.run();
            } catch (Throwable t) {
                // 发生异常则关闭整个EventExecutor
                logger.warn("Unexpected exception from an event executor: ", t);
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值