一、背景
这几天在看netty的源码,在看如下位置的时候,看到了创建一个监听器,然后看到了DefaultPromise和GlobalEventExecutor这两个类
DefaultPromise看起来不会特别复杂,只是多了一些设置成功失败等方法,然后可以唤醒listener,做对应的处理,这里的用法,其实就是将监听器里执行的内容提交到GlobalEventExecutor这个类中做调用
大家可以搜索下相关文章看看DefaultPromise,这个类不是本文的重点
Netty 之 DefaultPromise 源码解析:https://www.jianshu.com/p/d46c9c70e0ba(作者:jijs)
二、GlobalEventExecutor
稍微介绍下这个类
这个类是一个单例,他会自动开启线程,如果1s内没有任务被阻塞或者队列中没有任务了就会自动结束线程,请注意,他不适合处理大量的task
netty这里使用他来调起listener(listener一般不会太多,而且也不会是很复杂的业务逻辑)
1、疑问
我看了一篇这样的文章:
Netty笔记-GlobalEventExecutor:https://blog.youkuaiyun.com/waterseason/article/details/86769956(转载)
主线程中,一次性添加多个任务,执行的线程都是同一个,但如果每隔1秒钟添加任务,就会看到线程都是新创建的,我挺好奇是怎么做到的,所以认真研究了下这个类的工作原理!
2、整体的运行流程
(1)、所以完整的情况是,当我在外面调用submit提交task到GlobalEventExecutor中
Future<Integer> future=GlobalEventExecutor.INSTANCE.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("eventExecutor threadId:"+Thread.currentThread().getId()+
" inEventLoop:"+SelfGlobalEventExecutor.INSTANCE.inEventLoop());
return 1;
}
});
(2)、submit提交后,会调用到GlobalEventExecutor的execute方法
(3)、execute中会调用addTask(task),添加任务,并且判断inEventLoop,如果两个线程不一样的话(我在测试的时候,调用inEventLoop方法的都是main方法,和创建出来的thread方法都不是同一个),就会执行startThread(),创建新的线程
(4)、startThread方法会先判断started是否为false:
如果started为false就设置为true,然后通过threadFactory创建一个新的线程,并将taskRunner设置到新的线程中,最后调用线程的start方法
如果started为true,证明上一次创建的线程还在执行中,所以不需要做其他操作,等到之前那个线程拉取到新提交的task就可以
(5)、线程调用了start方法,就会调用TaskRunner内部类的run方法,run方法做了2件事情:
死循环通过takeTask这个方法中取出task,然后运行run,就会将队列中所有任务都拿出来执行
执行完当前所有task,将started设置为false,这样如果有新的task进来,就会新创建一个线程
(6)、takeTask这个方法里面也是一个死循环,但是里面有不少if和return
这里要说到,在初始化GlobalEventExecutor的时候,初始化了scheduledTaskQueue,并将quietPeriodTask放进去,通过这个来判断过了1s后,是否还有新的task(如何判断呢?请往下看)
(7)、如果takeTask方法中,没有从taskQueue里取出新的task了,那么就会调用fetchFromScheduledTaskQueue,将quietPeriodTask加入到taskQueue,并且被取出
(8)、然后在TaskRunner类中,由于task != quietPeriodTask,导致执行不了continue关键字,就会做后续的判断,将started设置为false,那么下次又有新的task来的时候,就会创建新的线程
3、回答疑问和总结
netty在父类的有序的Queue中,添加了一个1秒后到执行时间,并且每次执行间隔1秒,但是run方法里没有执行任何逻辑的ScheduledTask,并在takeTask方法中,拿到这个任务是否到执行时间,如果到了,直接poll任务,如果没到,就poll(超时时间)阻塞获取task任务,就是通过这样的方式来做那个1s的时间判断
如果想修改这个1s限制的话,就把如下的值改掉(比如改成10s)
那么10s内如果不停的有任务提交,执行的线程就不会停下来,就不会创建新的线程!
好厉害啊!感觉开眼了,我应该好好学习学习这些大佬写的代码~~
如果有同学想直接看源码的注释的话,可以参阅以下文章:
Netty源码分析-GlobalEventExecutor:https://blog.youkuaiyun.com/nimasike/article/details/102500491(作者:温故而知新666)