接口 EventLoop

所有超级接口:
AutoCloseable, EventLoopGroup, Executor, ExecutorService, FixedEventLoopGroup, IExecutor, IExecutorService, IScheduledExecutorService, Iterable<EventLoop>, ScheduledExecutorService, SingleThreadExecutor
所有已知实现类:
AbstractEventLoop, DisruptorEventLoop

@ThreadSafe public interface EventLoop extends FixedEventLoopGroup, SingleThreadExecutor
事件循环 它是单线程的,它保证任务不会并发执行,且任务的执行顺序和提交顺序一致。

时序

EventLoopGroup的基础上,我们提供这样的时序保证:
1.如果 task1 的执行时间小于等于 task2 的执行时间,且 task1 先提交成功,则保证 task1 在 task2 之前执行。
它可以表述为:不保证后提交的高优先级的任务能先执行。
还可以表述为:消费者按照提交成功顺序执行是合法的。
(简单说,提高优先级是不保证的,但反向的优化——降低优先级,则是可以支持的)

2.周期性任务的再提交 与 新任务的提交 之间不提供时序保证。
它可以表述为:任务只有首次运行时是满足上面的时序的。
如果你期望再次运行和新任务之间得到确定性时序,可以通过提交一个新任务代替自己实现。
(简单说,允许降低周期性任务的再执行优先级)

3. schedule系列方法的initialDelaydelay为负时,将转换为0。 fixedRate除外,fixedRate期望的是逻辑时间,总逻辑时间应当是可以根据次数计算的,转0会导致错误,因此禁止负数输入。 另外,fixedRate由于自身的特性,因此难以和非fixedRate类型的任务达成时序关系。

Q:为什么首次触发延迟小于0时可以转为0? A:我们在上面提到,由于不保证后提交的任务能在先提交的任务之前执行,因此当多个任务都能运行时,按照提交顺序执行是合法的。
因此,我们只要保证能按照提交顺序执行就是合法的,当所有的初始延迟都负转0时,所有后续提交的任务的优先级都小于等于当前任务, 因此后续提交的任务必定在当前任务之后执行,也就是按照提交顺序执行,因此是合法的。

警告

由于EventLoop都是单线程的,你需要避免死锁等问题。
1. 如果两个EventLoop存在交互,且其中一个使用有界任务队列,则有可能导致死锁,或大量任务超时。
2. 如果在EventLoop上执行阻塞或死循环操作,则可能导致死锁,或大量任务超时。
3. 如果EventLoop支持自定义等待策略,要小心选择或实现,可能导致定时任务不能被及时执行。
作者:
wjybxx date 2023/4/7
  • 方法详细资料

    • select

      @Nonnull default EventLoop select()
      从接口复制的说明: EventLoopGroup
      选择一个 EventLoop用于接下来的任务调度
      指定者:
      select 在接口中 EventLoopGroup
      返回:
      this - 由于EventLoop表示单个线程,因此总是分配自己。
    • select

      @Nonnull default EventLoop select(int key)
      从接口复制的说明: FixedEventLoopGroup
      通过一个键选择一个EventLoop 这提供了第二种绑定线程的方式,第一种方式是通过EventLoopGroup.select()分配一个线程,让业务对象持有EventLoop的引用。 现在,你可以为用户分配一个键,通过键建立虚拟绑定。
      指定者:
      select 在接口中 FixedEventLoopGroup
      参数:
      key - 计算索引的键;限定int可保证选择性能
      返回:
      this - 由于EventLoop表示单个线程,因此总是选中自己
    • parent

      @Nullable EventLoopGroup parent()
      返回该EventLoop线程所在的线程组(管理该EventLoop的容器)。 如果没有父节点,返回null。
    • inEventLoop

      boolean inEventLoop()
      测试当前线程是否是Executor所在线程。 主要作用: 1.判断是否可访问线程封闭的数据。 2.防止死锁。

      警告:如果用户基于该测试实现分支逻辑,则可能导致时序错误,eg:

       
       	if(eventLoop.inEventLoop()) {
       	    doSomething();
        } else{
       	    eventLoop.execute(() -> doSomething());
        }
       
       
      假设现在有3个线程:A、B、C,它们进行了约定,线程A投递任务后,告诉线程B,线程B投递后告诉线程C,线程C再投递,以期望任务按照A、B、C的顺序处理。 在某个巧合下,线程C可能就是执行者线程,结果C的任务可能在A和B的任务之前被处理,从而破坏了外部约定的时序。

      该方法一定要慎用,它有时候是无害的,有时候则是有害的,因此必须想明白是否需要提供全局时序保证!

      指定者:
      inEventLoop 在接口中 SingleThreadExecutor
    • inEventLoop

      boolean inEventLoop(Thread thread)
      测试给定线程是否是当前事件循环线程 1.注意:EventLoop接口约定是单线程的,不会并发执行提交的任务,但不约定整个生命周期都在同一个线程上,以允许在空闲的时候销毁线程。 如果当前线程死亡,EventLoop是可以开启新的线程的,因此外部如果捕获了当前线程的引用,该引用可能失效。 (有一个经典的示例:Netty的GlobalEventExecutor) 2.该方法可用于任务检测是否切换了线程,以确保任务运行在固定的线程中
      指定者:
      inEventLoop 在接口中 SingleThreadExecutor
    • wakeup

      void wakeup()
      唤醒线程 如果当前EventLoop线程陷入了阻塞状态,则将线程从阻塞中唤醒;通常用于通知线程及时处理任务和响应关闭。 如果线程已停止,则该方法不产生影响
    • mainModule

      EventLoopModule mainModule()
      事件循环的主模块 主模块是事件循环的外部策略实现,用于暴露特殊的业务接口 (Agent对内,MainModule对外,都是为了避免继承扩展带来的局限性)
    • ensureInEventLoop

      default void ensureInEventLoop()
      抛出:
      GuardedOperationException - 如果当前不在EventLoop所在线程
    • ensureInEventLoop

      default void ensureInEventLoop(String method)
      抛出:
      GuardedOperationException - 如果当前不在EventLoop所在线程
    • state

      返回:
      EventLoop的当前状态
    • isRunning

      boolean isRunning()
      是否处于运行状态
    • runningFuture

      IFuture<?> runningFuture()
      等待线程进入运行状态的future future会在EventLoop成功启动的时候进入完成状态

      1.如果EventLoop启动失败,则Future进入失败完成状态 2.如果EventLoop未启动直接关闭,则Future进入失败完成状态 3.EventLoop关闭时,Future保持之前的结果

    • start

      IFuture<?> start()
      主动启动EventLoop 一般而言,我们可以不主动启动EventLoop,在提交任务时会自动启动EventLoop,但如果我们需要确保EventLoop处于正确的状态才能对外提供服务时,则可以主动启动时EventLoop。 另外,通过提交任务启动EventLoop,是无法根据任务的执行结果来判断启动是否成功的。
      返回:
      runningFuture()