netty SingleThreadEventExecutor 类 addTaskWakesUp 成员变量详解

目录

1.addTaskWakesUp解释

2.DefaultEventExecutor

3.NioEventLoop

4.总结


1.addTaskWakesUp解释

SingleThreadEventExecutor 类中有一个成员变量 addTaskWakesUp 。网上的解释一般都是:这个参数的说明在调用addTask(Runnable)添加任务时是否唤醒线程。这种理解是错误的,真正的理解正好相反。调用addTask(Runnable)添加任务时是否能唤醒线程决定了addTaskWakesUp 是true还是false。

2.DefaultEventExecutor

DefaultEventExecutor 是 SingleThreadEventExecutor 的默认实现,按顺序执行提交的任务。

    // ====DefaultEventExecutor.java========
    @Override
    protected void run() {
        for (;;) {
            // 循环取任务并执行
            Runnable task = takeTask();
            if (task != null) {
                task.run();
                updateLastExecutionTime();
            }

            if (confirmShutdown()) {
                break;
            }
        }
    }

run() 方法循环执行 takeTask() 从 taskQueue 取任务并执行。

    //========SingleThreadEventExecutor.java========    
    // 从taskQueue中取下一个任务,如果当前没有则阻塞
    // taskQueue必须是BlockingQueue类型,否则会抛UnsupportedOperationException异常
    protected Runnable takeTask() {
        assert inEventLoop();
        if (!(taskQueue instanceof BlockingQueue)) {
            throw new UnsupportedOperationException();
        }

        BlockingQueue<Runnable> taskQueue = (BlockingQueue<Runnable>) this.taskQueue;
        for (;;) {
            ScheduledFutureTask<?> scheduledTask = peekScheduledTask();
            // scheduledTaskQueue中没有延时任务
            if (scheduledTask == null) {
                Runnable task = null;
                try {
                    // 阻塞在这里,直到taskQueue中有新的任务
                    task = taskQueue.take();
                    if (task == WAKEUP_TASK) {
                        task = null;
                    }
                } catch (InterruptedException e) {
                    // Ignore
                }
                return task;
            } else {//scheduledTaskQueue中有延时任务
                // 获取scheduledTask的到期时间
                long delayNanos = scheduledTask.delayNanos();
                Runnable task = null;
                if (delayNanos > 0) {
                    try {
                        // 阻塞在这里,直到第一个延时任务时间到了或者有新的任务
                        task = taskQueue.poll(delayNanos, TimeUnit.NANOSECONDS);
                    } catch (InterruptedException e) {
                        // Waken up.
                        return null;
                    }
                }
                // task == null说明poll的阻塞时间delayNanos到了,taskQueue中没有新的任务
                if (task == null) {
                    // We need to fetch the scheduled tasks now as otherwise there may be a chance that
                    // scheduled tasks are never executed if there is always one task in the taskQueue.
                    // This is for example true for the read task of OIO Transport
                    // See https://github.com/netty/netty/issues/1614
                    // 阻塞时间到了,没有新的任务,
                    //需要用fetchFromScheduledTaskQueue方法将延时队列中到时间的任务放入taskQueue中
                    fetchFromScheduledTaskQueue();
                    task = taskQueue.poll();
                }

                if (task != null) {
                    return task;
                }
            }
        }
    }

在 taskQueue 阻塞的时候,如果添加新的任务,taskQueue会被唤醒,从而run方法可以继续执行。这样就实现了添加任务唤醒线程的作用。所以DefaultEventExecutor的构造方法中会传给父类addTaskWakesUp=true;

接下来看下DefaultEventExecutor实例:

    @Test
    public void execute() throws Exception {
        final DefaultEventExecutor eventExecutors = new DefaultEventExecutor();
        new Thread(() -> {
            eventExecutors.execute(() -> System.out.println("start thread ===>>> " + Thread.currentThread().getName()));
        }).start();
        TimeUnit.SECONDS.sleep(1);
        // 通过Execute方法添加任务
        eventExecutors.execute(() -> {
            System.out.println("task1 run with ===>>>" + Thread.currentThread().getName());
        });
        TimeUnit.SECONDS.sleep(30);
    }

通过execute方法添加并执行任务,接下来看下Execute方法

    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        
        boolean inEventLoop = inEventLoop();
        // 添加任务,从上面的分析可以知道添加任务之后,阻塞会被唤醒,从而执行任务
        addTask(task);
        if (!inEventLoop) {
            startThread();
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {
                    // The task queue does not support removal so the best thing we can do is to just move on and
                    // hope we will be able to pick-up the task before its completely terminated.
                    // In worst case we will log on termination.
                }
                if (reject) {
                    reject();
                }
            }
        }
        // 由于任务在添加的时候,阻塞已被唤醒,这里就不需要主动wakeup(inEventLoop);
        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }

由于DefaultEventExecutor是通过BlockingQueue的阻塞来实现唤醒的,所以addTaskWakesUp=true。

因此,不需要执行wakeup(inEventLoop)主动唤醒。

3.NioEventLoop

对于NioEventLoop来时,阻塞的是selector.select()方法,该阻塞在添加任务时不能被唤醒,所以需要执行 wakeup(inEventLoop)主动唤醒。

所以,addTaskWakesUp=false。

4.总结

addTaskWakesUp 不是用来控制“添加任务时是否唤醒线程”的,而是根据类能实现的功能,来设置addTaskWakesUp的。

如果addTask(Runnable)添加任务时能唤醒线程,那么addTaskWakesUp=true;

如果addTask(Runnable)添加任务时不能唤醒线程,那么addTaskWakesUp=false;

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值